diff --git a/things-i-learnt/src/personal/bad-code-defense.md b/things-i-learnt/src/personal/bad-code-defense.md index 5a8bf1e..56a37f4 100644 --- a/things-i-learnt/src/personal/bad-code-defense.md +++ b/things-i-learnt/src/personal/bad-code-defense.md @@ -1 +1,19 @@ # Don't Defend Bad Code + +Bad code exists everywhere. You shouldn't defend it, even if it is your own +code. + +Bad code isn't bad on purpose. It sadly happens. But because it is bad, you +shouldn't defend it. + +For example, an application does whatever you need. But it crashes from time +to time. Software shouldn't crash and you shouldn't defend it just because it +does whatever you need. + +Your internal application works on a single browser. That's bad. "But maybe +the other devs thought it wouldn't be worth working on all browsers". No. It +is _bad_. You shouldn't defend the other devs because they decided to focus on +a single browser due whatever problems they were facing. Sure it wasn't nice +that they had to do this trade-off, but it is still _bad_ software. + +If we keep defending this kind of software, we will still get bad software. diff --git a/things-i-learnt/src/personal/blogging.md b/things-i-learnt/src/personal/blogging.md index 035723d..aa378de 100644 --- a/things-i-learnt/src/personal/blogging.md +++ b/things-i-learnt/src/personal/blogging.md @@ -1 +1,23 @@ # Blogging About Your Stupid Solution Is Still Better Than Being Quiet + +You may feel "I'm not start enough to talk about this" or "This must be so +stupid I shouldn't talk about it". Don't. + +Create a blog. Post about your stupid solutions. They are still smarter than +someone else's solution. + +Also, come back later and fight your own solutions with better ones. + +Show your growth. + +But do yourself a favour and turn off comments. Unfortunately, the internet is +a toxic place and the fears you may have are created by a small portion of it +that doesn't care about people learning. + +Focus on your work. Focus on whatever you are thinking. Post about your +speculations if something would work. Revisit them later. Answer yourself. All +that will show that you're interested in the field and will count points +towards you. + +There are several options on where to blog; even Github/Gitlab can be used to +blogging, using their Pages features. diff --git a/things-i-learnt/src/personal/coc.md b/things-i-learnt/src/personal/coc.md index afef92c..5e44cae 100644 --- a/things-i-learnt/src/personal/coc.md +++ b/things-i-learnt/src/personal/coc.md @@ -1 +1,17 @@ # Code of Conduct Protect YOU, Not THEM + +When you're beginning with any language/library/framework, check their CoC; +they will protect _you_ from being harassed for not immediately getting what +is going on instead of blocking you from telling them what you think. + +I'm mentioning this 'cause a lot of people complain about CoC, but they +forget that they allow them to join in any project without being called +"freaking noob" or "just go read the docs before annoying us". + +And don't be afraid to ask moderators if someone that seems be in the +community for longer than you isn't break the CoC. Just because you just got +into the community, it doesn't mean you can't be part of it or that someone +can abuse their position of "veteran" to not respect you. + +Also, remember that most people that are against CoCs are the ones that want +to be able to call names on everyone. diff --git a/things-i-learnt/src/personal/fixable.md b/things-i-learnt/src/personal/fixable.md index 43a3191..b4b42db 100644 --- a/things-i-learnt/src/personal/fixable.md +++ b/things-i-learnt/src/personal/fixable.md @@ -1 +1,21 @@ # Toxic/Aggressive People Are Not Fixable -- Unless It's You + +You may think "But I could go to those people and say 'Why are you being +toxic?' or 'Why are you attacking me?' or even just tell them it's not nice to +say such things. It would help." + +I don't believe that's the case. + +In the case of toxic people, they just want to see the place burn so they can +be the hero that saves the day. Microaggressors just want to make your feel +down 'cause so they could feel superior to you. + +And I don't think they can be easily fixable. That's their modus operandi and +they will keep doing it so 'cause that's the only way they can see to +"improve" themselves -- even if there is no real improvement, they are just +letting everything down so they seem better than the others. + +On the other hand, if you keep [paying attention to the way people react to +you](./watch-reactions.md), you may notice that you may be +doing this to others. And now the ball is in your park: Do you want to be a +better person or not? diff --git a/things-i-learnt/src/personal/hero-syndrome.md b/things-i-learnt/src/personal/hero-syndrome.md index 84601e0..b020441 100644 --- a/things-i-learnt/src/personal/hero-syndrome.md +++ b/things-i-learnt/src/personal/hero-syndrome.md @@ -1 +1,20 @@ # Don't Confuse Hero Project With Hero Syndrome + +Someone that suffers from Hero Syndrome will claim that things won't work +unless they are carefully watching over everything. + +I've seen this at least two times in my professional life. Usually, those +people are actually doing so much micromanaging that they are not other +realize when things are in trouble. + +I've even seen someone doing a poor job on _their job_, so things would break +and then start calling people out that he had to fix it 'cause nobody would. + +Don't do that. + +I know you can get frustrated when you're the only one realizing things are +breaking apart, but you can add some +[monitoring](../programming/running/monitoring.md) to your project, asking your +manager to show your solution -- you can even mention it on your group/daily +stand up meeting -- and pointing out to people how to realize when your +project broke. Suddenly, people will realize how to monitor theirs too. diff --git a/things-i-learnt/src/personal/learn-about-yourself.md b/things-i-learnt/src/personal/learn-about-yourself.md index f6bfa9b..df86ec4 100644 --- a/things-i-learnt/src/personal/learn-about-yourself.md +++ b/things-i-learnt/src/personal/learn-about-yourself.md @@ -1 +1,25 @@ # You'll Learn About Yourself The Hard Way + +We get frustrated with code that doesn't compile. We get angry with customers +asking things back and forth. We get upset when upper management can't make up +its mind. And we lash out on others when that happens. + +Unfortunately, that's the way you'll learn about yourself. You'll learn to +figure out when you get frustrated. You'll learn how you can keep getting +attacked by others -- even micro-aggressions -- but, without realizing, at +some point you'll suddenly burst into an counter-attack that may seem out of +proportion. + +The worst part of it all? Very few people will tell you what you're doing +before it's too late -- you'll get in trouble way before you could actually +understand what you were doing. + +But, again, you'll get in trouble. + +And you _must_ learn about these events. Take the feedback as true, even if +you don't agree with it. I had my falls, and I always took it as something I +need to improve, even if later I said to my psychologist that I thought it was +unfair and biased -- mostly 'cause I'm the quiet guy that always took the +blows and never complained about, and people taking the flak from me were more +vocal and friendlier to the higher-ups. But, again, I took it as true, and did +my best to work on those problems. diff --git a/things-i-learnt/src/personal/microaggressions.md b/things-i-learnt/src/personal/microaggressions.md index 101d6d0..5e049f4 100644 --- a/things-i-learnt/src/personal/microaggressions.md +++ b/things-i-learnt/src/personal/microaggressions.md @@ -1 +1,45 @@ # Beware of Microaggressions + +Microaggressions are defined as "brief, everyday exchanges that send +denigrating messages to certain individuals because of their group +membership". The hardest part is that they don't sound aggressive. + +Although I'm not part of an oppressed group, I've been microattacked more than +once, by the same person. + +At some point, he mentioned that one couldn't talk bad about someone "around +some people". That "someone" was a politician that got arrested for, +basically, stealing from the country to promote a certain other company. The +way that person said it, thought, made it seem I felt it was wrong to arrest +the politician. + +Another time, I was casually saying that I shouldn't have come to work on my +motorbike, although three forecast apps said it wouldn't rain. His comment: "I +just looked outside". + +It may seem innocuous reading those, but if you look closely, all he was +trying to do was do let me down. Oh no, I'm part of a group that thinks +politicians shouldn't be arrested! Oh no, I'm not smart enough to look +outside, while he is![^1]. + +And those are really hard to fight, 'cause we aren't prepared to "get" those +as real attacks. + +On top of that, HR people are not really prepared to understand those (it will +always fall into the "it was just a joke"[^2] excuse and you'll be the +troublemaker[^3]). + +The worst part? While you don't fully get it as an attack, it will slowly pile +up. At some point, you may even burst into attacking the person back, with all +the concentrated attacks in a single moment. And them _you_ will be seen as +aggressive, not them. + +Better just stay away and avoid contact if possible. + +[^1]: In the end, when I had to leave work to go back home, it wasn't raining. + +[^2]: ... which is the pure definition of "Schoddinger's Asshole": Someone + that make a comment and, by the other people reaction, call it a joke as a + "get out of jail" card. + +[^3]: And I do wish you have a HR department that understand microaggressions. diff --git a/things-i-learnt/src/personal/not-done.md b/things-i-learnt/src/personal/not-done.md index d846141..293e64e 100644 --- a/things-i-learnt/src/personal/not-done.md +++ b/things-i-learnt/src/personal/not-done.md @@ -1 +1,23 @@ # Don't Tell It's Done When It's Not + +You are tired of running the same thing over and over again. You kinda +remember that something weird may happen, but because you're tired, you tell +everyone that "It's finished". Don't. + +I must admit that I've done this. Yes, there is that corner case that I didn't +test. Yes, there is that piece of code that, although it works, it's weird. +Yes, I left a technical debt behind. But we get tired of looking at the same +thing over and over. And we get pushed by higher ups to deliver stuff. And +that's when we say "It's done", when it's not. + +Although we just dodged one, we end up leaving our colleagues -- people that +really depend on our work -- down. They expect to connect to whatever we are +doing to keep going. The expect to see the results of their work crystallized +by ours. And they can't. 'Cause we said "It's done" when it wasn't. + +And you can be sure, as soon as that happens, they will be the first to point, +in minutes, that it is not done. + +On the other hand, if you say "Almost, but there is this thing that bothers me +and I think it may give us a headache in the future", they will understand. So +be clear and transparent about your work. diff --git a/things-i-learnt/src/personal/own-your-shit.md b/things-i-learnt/src/personal/own-your-shit.md index 032602b..c014aac 100644 --- a/things-i-learnt/src/personal/own-your-shit.md +++ b/things-i-learnt/src/personal/own-your-shit.md @@ -1 +1,36 @@ # Own Your Shit + +When I said "Scala is garbage" or "Gerrit is a mistake", it wasn't "l33th4x0r" +who said that; it was Julio Biason. 'Cause I do believe that putting your face +to be slapped is the way we grow. + +I do understand -- and you must have realized reading some of the previous +points when I talk about it -- that privacy is important. For some people, +hiding their real name is important for lots of reasons. + +But I also personally believe that using some weird name and some face that +isn't yours on your avatar may give you a false sense of "that person is the +guilty one, not me" when it comes to criticism. + +I mean, yes, I hate Scala with a passion. I do understand _why_ the language +developers decided to come with some options about it, and I still think those +decisions were stupid and you should never sacrifice readability for +ease-to-use. + +But it wasn't some random person using a weird name full of numbers and an +avatar of some anime saying Scala is garbage. My name is even in this blog +name, in the URL and in every social network I use there is a picture of me. + +So yeah, Julio said Scala is garbage. + +There is another thing about using your real name and real face: I'm not just +saying "Scala is garbage", I have to back that up with some facts -- or, in +this case, personal opinions -- about it. It's not simply an attack to Scala, +is a somewhat thought out attack on Scala. + +And, on top of that, someone will one day come to me and say "Yeah Julio, that +thing you said about Scala: It is that way because of this, this and this". +And I'll probably have to say "You know, you're right: I was wrong." Because I +can't simply move my mistake to some other personality; who was wrong was +_me_. And unless I own my shit up, I'd never get the understanding I'd need to +see my mistake about Scala in the first place. diff --git a/things-i-learnt/src/personal/people-care.md b/things-i-learnt/src/personal/people-care.md index 5e040db..7ff0122 100644 --- a/things-i-learnt/src/personal/people-care.md +++ b/things-i-learnt/src/personal/people-care.md @@ -1 +1,12 @@ # People Get Upset About Code And Architecture Quality 'Cause They Care + +At some point, you'll describe some solution/decision about some piece of +code or some architectural design and people will seem annoyed/pissed about +it. When people care about a product/code, they do that. + +Or maybe _you_ will get annoyed/pissed. + +I think one of the nicest compliments I ever got was "You're annoying 'cause +you care" when we left a meeting in which we decided to cut corners and do +things halfway to beat some deadline -- or just 'cause people were not in the +mood to do things in a more complete way. diff --git a/things-i-learnt/src/personal/post-solution.md b/things-i-learnt/src/personal/post-solution.md index 3d3ee8b..0f642b6 100644 --- a/things-i-learnt/src/personal/post-solution.md +++ b/things-i-learnt/src/personal/post-solution.md @@ -1 +1,24 @@ # Don't Hide Your Stupid Solution + +You may think "This project is so small and so focused on whatever I needed, I +should never post it on Github. What would people think?" Github is not for +that. + +Github is not a repository for "cool, almost perfect" projects. You're free to +show that, at some point, you were a beginner[^1]. + +You can always come back, review what you did and fix it. It will, as your +[blog](./blogging.md), show that you're improving. + +... or maybe you'll let your project there just to rot. I still have some +Python projects that I wrote when I was learning the language that, although +they work, they don't look like Python projects. + +But who knows? Maybe the code you wrote to solve your small problem can help +someone else to fix their problem, which was not exactly the same, but pretty +close. Or even you could get a code review that would teach you something new +about the language/design you used. + +[^1]: Whoever see the first projects I did in + [Rust](https://www.rust-lang.org/) wouldn't think I have 30 years of + experience in the field. Everybody is a beginner at some point. diff --git a/things-i-learnt/src/personal/quit.md b/things-i-learnt/src/personal/quit.md index 6d776cf..dc6f3a9 100644 --- a/things-i-learnt/src/personal/quit.md +++ b/things-i-learnt/src/personal/quit.md @@ -1 +1,20 @@ # Realize When It's Time To Quit + +Instead of taking the blows and keep moving, maybe it would be better to your +own health to simply quit. + +Unexpected circumstances caused a delay on your task and your boss lashed at +you. + +You need to keep avoiding a guy that keeps bad mouthing some minority, +something that you don't agree. + +Another guy keeps an aggressive posture around women, and you know that's not +something nice to do. + +Yet a third one keeps complaining that, when he's not around, things don't +work. + +I've to say it: You're in a toxic environment. Even if the pay is nice and the +project is interesting, it's not worth your health. You'd end up being a +constantly pissed off, annoyed person on your forties (_cough_). diff --git a/things-i-learnt/src/personal/responsible-code.md b/things-i-learnt/src/personal/responsible-code.md index 78749c8..f8e9805 100644 --- a/things-i-learnt/src/personal/responsible-code.md +++ b/things-i-learnt/src/personal/responsible-code.md @@ -1 +1,38 @@ # Take Responsibility For The Use Of Your Code + +This is hard. Very very hard. It's the difference between "freedom" and +"responsibility". + +There is nothing wrong in writing, for example, a software to capture people's +faces and detect their ethnicity, but you have to think about what that will +be used on. + +Even on an open source project, you can take responsibility without blocking +people. You can make your project harder for people trying to abuse to use it, +to the point they will have to take control of their own fork. + +One example is a small application called [Tusky](https://tusky.app/), which +is "An Android client for the microblogging server Mastodon", completely open +source. Mastodon is a network of microblogging servers with connect to each +other, kinda like Twitter, but you can pick a different server that is not +twitter.com and still get updates from that server. One of the servers that +appeared in the server list is an alt-right server which, as most alt-right +forums, promote a lot of hate. What Tusky did? When you try to add an account +on that server, instead of adding the account, [they play a video of Never +Gonna Give You Up](https://github.com/tuskyapp/Tusky/pull/1303), basically +[rickrolling](https://en.wikipedia.org/wiki/Rickrolling) anyone who, well, is +an alt-righter. + +Tusky broke the open source license? No, the code is still available. Anyone +wanting to use the server can pick the code, fork it, remove the rickroll and +release their own version of the application. But Tusky developers took an +instead of saying "We'll not take part in promoting hate speech" and one can't +deny that they did. + +It is a bit hard to do so on the company code -- you would get some reprimands +if you try to shame or block one of the company clients from using the company +application -- but you [can say no](./say-no.md) and, +depending on how offensive you think the use the code is, you can even start +looking for a new place to work. People on larger and "cooler" companies, like +Google, left their jobs because they didn't agree with what the company was +doing, and so can you. diff --git a/things-i-learnt/src/personal/say-no.md b/things-i-learnt/src/personal/say-no.md index 89f2a15..dc4eab4 100644 --- a/things-i-learnt/src/personal/say-no.md +++ b/things-i-learnt/src/personal/say-no.md @@ -1 +1,17 @@ # Learn To Say No + +Sometimes, you'll have to say no: No, I can't do it; no, it can't be made in +this time; no, I don't feel capable of doing this; no, I don't feel +comfortable writing this. + +Saying no doesn't mean you won't do it. Once I had to say to our CTO: "Ok, +I'll do it, but I want to note that I don't agree with what we are doing." In +the end, the app was barred exactly because the thing we were doing. + +Being explicit about what you don't feel is a good point may not be what some +higher ups are expecting. The fact that you don't approve but will do it +anyway may be something that can either show that your not simply a drone or, +unfortunately, label you as a "troublemaker". Honestly, if you feel it threw +you in the former, you should start looking for a new place to work. If you +said you won't be comfortable and still _did the work_, and they had to label +you something, then this place doesn't respect you as a person. diff --git a/things-i-learnt/src/personal/small-world.md b/things-i-learnt/src/personal/small-world.md index ec13d59..ed65eea 100644 --- a/things-i-learnt/src/personal/small-world.md +++ b/things-i-learnt/src/personal/small-world.md @@ -1 +1,26 @@ # I.T. World Is Really Small + +We have two expressions here: "The world turns around"; it means whatever you +do, sometime in the future, you'll face the consequences of it. Another +expression is "The world of _something_ is an egg"; because the world turns +around, if the world is an egg, you'll face the consequences sooner than you +think. + +What do I meant with those two expressions? + +Well, first thing, if you do a bad job, if you don't care about your +co-workers, if you're not a team player, if you keep bad mouthing someone... +You'll find someone that heard about the things you do and may damage your +reputation. + +So be nice and a team player. + +Just to be clear: Yes, I did my fair share of not being a team player and bad +mouthing people[^1] and I'm pretty sure there are companies around that would +never hire me 'cause someone inside heard that I bad mouth someone or didn't +do as a team player in some other place. I try to avoid doing it so as much as +I can but, hey, I'm just human. + +[^1]: I still call actions of previous colleagues around even to this day. If + I'm bad mouthing or just telling what happened is up to whoever is listening + to me. diff --git a/things-i-learnt/src/personal/specialists.md b/things-i-learnt/src/personal/specialists.md index 1a0e35c..fc35226 100644 --- a/things-i-learnt/src/personal/specialists.md +++ b/things-i-learnt/src/personal/specialists.md @@ -1 +1,33 @@ # Companies Look For Specialists But Keep Generalists Longer + +If you know a lot about one single language, it may make it easier to get a +job, but in the long run, language usage dies or loses its charms and you'll +need to find something else. Knowing a bit about a lot of other languages +helps in the long run, not to mention that may help you think of better +solutions. + +Even if you're in a shop that is mainly in one single language, that's no +excuse to not check other languages. But, then again, learning languages that +are just small changes on the current language would not help you either. + +Alan Perlis, author of the ALGOL language, has one excellent phrase: "A +language that doesn't affect the way you think about programming, is not worth +knowing." + +I still maintain one single rule for programming languages: The language I use +at work must not be the same language I use outside it[^1]. That simple rule +made sure I was always learning something new. + +Learning a new language can also help you understand things in some language +you used before: Rust help me understand how generics works in Java; seeing +how to do dependency injection in C++ help me understand how Spring does it in +Java. + +On top of that, because I was always learning something new, moving between +projects was something that happened a lot. At one point, I was hired to work +with Python, but the contract was taking too long to be signed, and my manager +asked if I could help some other team with their iOS application. Because I +did learn a bit about Objective-C, surely I could help. Later, another project +in C show up and guess who also knew C? + +[^1]: ... which led me into some sad times when I was working with Python. diff --git a/things-i-learnt/src/personal/stupid-bugs-list.md b/things-i-learnt/src/personal/stupid-bugs-list.md index e7303fc..07ba95d 100644 --- a/things-i-learnt/src/personal/stupid-bugs-list.md +++ b/things-i-learnt/src/personal/stupid-bugs-list.md @@ -1 +1,14 @@ # Keep A List of Stupid Bugs That Took More Than 1 Hour To Solve + +If it took you more than one hour for you to figure out what went wrong, it is +a good idea to put it on list, 'cause these things have the tendency to appear +again. + +I must admit that this is one thing that I should be doing, but I keep +forgetting. The worst part: It usually takes me about an hour to figure out +what went wrong, only to realize by the solution that I had the same problem +(with the same solution) before and it took me about one hour to figure out +what went wrong. + +If I just had a list of stupid bugs that took me about 1 hour or more to +solve, I wouldn't be stuck for another hour figuring out what went wrong. diff --git a/things-i-learnt/src/personal/things-i-dont-know.md b/things-i-learnt/src/personal/things-i-dont-know.md index b9d35cc..1c57602 100644 --- a/things-i-learnt/src/personal/things-i-dont-know.md +++ b/things-i-learnt/src/personal/things-i-dont-know.md @@ -1 +1,12 @@ # Keep A List of Things I Don't Know + +Richard Feymann, famous physicist, kept a notebook with the title "Things I +Don't Know". + +I keep a similar "Task List" for myself. If some technology starts appearing +everywhere or something grabs my attention, but I don't have the time to +research it, I put it on this task list. + +When I start my research, I keep some notes together, although [not on +paper](../organization/paper-notes.md), so I can use as reference in the +future. diff --git a/things-i-learnt/src/personal/time-to-stop.md b/things-i-learnt/src/personal/time-to-stop.md index 8d2bed9..80e89cb 100644 --- a/things-i-learnt/src/personal/time-to-stop.md +++ b/things-i-learnt/src/personal/time-to-stop.md @@ -1 +1,15 @@ # When It's Time to Stop, It's Time To Stop + +Learn when you can't code anymore. + +Learn when you can't process things anymore. + +Don't push beyond that, it will just make things worse in the future. + +I tried to keep coding once when I had a migraine (not strong, but not mild). +Next day, when I was better, I had to rewrite most of the stuff I did, 'cause +it was all shit. + +Also, when you're not feeling fine, you won't be able to concentrate and your +productivity will plunge. It's a lot better to be a burden to society at your +own place than a burden to society at work. diff --git a/things-i-learnt/src/personal/time.md b/things-i-learnt/src/personal/time.md index fdc2b73..fa8e187 100644 --- a/things-i-learnt/src/personal/time.md +++ b/things-i-learnt/src/personal/time.md @@ -1 +1,27 @@ # You Always Have The Time + +You may think "Alright, I have a list of things I don't know, but I have no +time to learn those things!" You do have time. + +Most of this blog/book was written during my lunch breaks; I can have my lunch +in 30 minutes, and then I still have 20-30 minutes free for myself. In those +lunch breaks, I wrote a very stupid application in Rust to download some stuff +I wanted. + +I don't fall asleep straight away, it still takes me about 30 minutes to +actually feel sleepy. That's when I pick my tablet and read a book, which most +of the time is technical book, about some technology I'm interested in. + +Even if when I get home I don't feel like sitting in front of a computer to +code/write something, I always have the time to slowly progress. + +And that's how I always have the time. + +Sure, I could take those 30 minutes after lunch just to play games. I could +put myself to sleep watching Netflix. But, then again, I'd never wrote this +bunch of words, would never have an automated downloader and would not learn +about new stuff that people are talking about. + +Maybe people think "If I don't finish, it's over". Your life doesn't end in +one day. You still have tomorrow to keep going -- _to keep going_, not to +start. diff --git a/things-i-learnt/src/personal/toxic-people.md b/things-i-learnt/src/personal/toxic-people.md index 4dc5879..c5606b1 100644 --- a/things-i-learnt/src/personal/toxic-people.md +++ b/things-i-learnt/src/personal/toxic-people.md @@ -1 +1,24 @@ # Beware of Toxic People + +You'll find people that, even if they don't small talk you, they will bad +mouth everything else -- even some other people -- openly. + +Toxic people love drama. They love to put people down. They love to point +mistakes made by others -- but never by themselves. Some of them actually do +that to make themselves look better in the eyes of the upper management. + +Not totally toxic, but I did work with people who would never answer an email +unless the manager was in the discussion. Another person would always claims +his team did everything they could, even putting himself at the disposal of +the manager to solve any issues, and that the problem was not related to their +work -- which we proved three times it was. + +You need to stay away from those people. They will harm in ways you can figure +out immediately. Their attitude towards other (and maybe even yourself) will +drive you so down you'll waste more time wondering what you did wrong than +doing your job. + +One thing to take a lot of care: Even if it is not your intention, you may not +realize that you may be seen as toxic 'cause [you don't understand yourself +yet](./learn-about-yourself.md) and the way [people react to +you](./watch-reactions.md). diff --git a/things-i-learnt/src/personal/watch-reactions.md b/things-i-learnt/src/personal/watch-reactions.md index 1ea45ba..8698e54 100644 --- a/things-i-learnt/src/personal/watch-reactions.md +++ b/things-i-learnt/src/personal/watch-reactions.md @@ -1 +1,21 @@ # Pay Attention On How People React To You + +One way you can learn about yourself is to pay attention on how people react +to your actions. + +I have a "angry man resting face", which means that, even when I'm in a null +mood, it looks like I'm angry. + +I already had one meeting and which I started to ask something and noticed +that the other person move a bit back. That's when I realized that didn't +sound exactly how I meant. I had to add "I'm not saying what you're proposing +is wrong, I'm just confused." + +Also, I got a manager once come up with "I thought you were _the_ serious +person... till you suddenly started singing in the middle of a meeting"[^1]. + +You need to keep an eye on this. How is people reacting to your reactions? Are +they opening themselves to you or are they closing? + +[^1]: I have this "serious" problem that, depending on the word someone says, + I recall some lyrics and suddenly start singing it. diff --git a/things-i-learnt/src/programming/before/cognitive-cost.md b/things-i-learnt/src/programming/before/cognitive-cost.md index e5eff72..6a4eee2 100644 --- a/things-i-learnt/src/programming/before/cognitive-cost.md +++ b/things-i-learnt/src/programming/before/cognitive-cost.md @@ -1 +1,46 @@ # Cognitive Cost Is The Readability Killer + +"[Cognitive dissonance](https://en.wikipedia.org/wiki/Cognitive_dissonance)" +is a fancy way of saying "I need to remember two (or more) different and +contradicting things at the same time to understand this." Keeping those +different things in your head creates a cost and it keeps accumulating the +more indirect the things are ('cause you'll have to keep all those in your +head). + +(Disclaimer: I like to use the expression "cognitive dissonance" to make me +sound smarter. I usually explain what it means, though.) + +To give you an example of a (very mild) cognitive cost, I'll show you this: + +* You have a function called `sum()`. It does the sum of the numbers of a + list. +* You have another function, called `is_pred()`. It gets a value and, if it + fits the predicate -- a test, basically -- returns True; otherwise, + returns False. + +So, pretty simple, right? One function sums numbers and another returns a +boolean. + +Now, what would you say if I shown you this, in Python: + +```python +sum(is_pred(x) for x in my_list) +``` + +Wait, didn't I say that `sum()` sums numbers? And that `is_pred()` returns a +boolean. How can I sum booleans? What's the expected result of True + True + +False? + +Sadly, this works. Because someone, long time ago, didn't think booleans were +worth a thing and used an integer instead. And everyone else since then did +the same stupid mistake. + +But, for you, you'll now read a line that says "summing a boolean list returns +a number". And that's two different, disparate things that you suddenly have +to keep in mind when reading that line. + +That's why [types are important](../coding/data-types.md). Also, this may +sound a bit like [the magical number seven](./magical-number-seven), 'cause +you have to keep two things at your mind at the same thing but, although +that's not near seven, they are not the same, with opposite (for weird +meanings of "opposite", in this case) meanings. diff --git a/things-i-learnt/src/programming/before/data-flow.md b/things-i-learnt/src/programming/before/data-flow.md index c7355d9..eaf6971 100644 --- a/things-i-learnt/src/programming/before/data-flow.md +++ b/things-i-learnt/src/programming/before/data-flow.md @@ -1 +1,25 @@ # Thinking Data Flow Beats Patterns + +When you're trying to find a solution to your problem, think on the way the +data will flow through your code. + +Instead of focusing on design patterns, a better way is to think the way the +data will flow -- and be transformed -- on your code. + +For example, the user will input a number. You'll get this number and find the +respective record on the database. This is a transformation -- no, it's not +"I'll get the number and receive a complete different thing based upon it", +you're actually transforming the number into a record, using the database as a +transformation. + +(Yes, I know, it's not that clear at the first glance, but you have to think +that they are the same data with different representations.) + +Most of the time I did that, I managed to come with more clear design for my +applications. I didn't even think about how many functions/classes it would be +needed to do these kind of transformations, that was something I came up +_after_ I could see the data flow. + +In a way, this way of thinking gets things more clear 'cause you have a list +of steps of transformations you need to do, so you can write them one after +another, which prevents a lot of bad code in the future. diff --git a/things-i-learnt/src/programming/before/debuggers.md b/things-i-learnt/src/programming/before/debuggers.md index a3a27b5..22c3c42 100644 --- a/things-i-learnt/src/programming/before/debuggers.md +++ b/things-i-learnt/src/programming/before/debuggers.md @@ -1 +1,30 @@ # Debuggers Are Overrated + +I heard a lot of people complaining that code editors are bad 'cause it's hard +to attach a debugger. I'd claim that this vision is wrong. + +But let's take a thing out of the way beforehand: I'm not saying debuggers are +_bad_ you should never use them. Debuggers have their use, but every time I +had to use one, it was because there was something missing. + +Most recently, using a framework in Java, I had problems with my code. I'd +expect it [to crash](./crash-it) 'cause I didn't handle +things. What actually happened is that the framework silently hid the error +and restarted the processing. To find out what was happening, I had to attach +a debugger and see what was wrong with the data; otherwise, I'd have no idea +about what's wrong. + +Was a debugger necessary there? I don't think so. If the framework actually +displayed the error (crashed, put a wall of text on the logs, whatever), I +wouldn't need to use a debugger. But, because something was missing, I did, +in fact, was _forced_ to use a debugger. + +Besides this, in the long run, you'd end up with problems in locations that +you can't attach a debugger -- for example, your production environment. You +_could_ but you _shouldn't_ do this. On the other hand, if you [log +events](./log-events), then you can see what was going +on, without a debugger. + +Again, I'm not taking the merits of debuggers, but in the long run, they are +mostly useless and actually point missing surrounding support to actually +understand what's going on. diff --git a/things-i-learnt/src/programming/before/functional-programming.md b/things-i-learnt/src/programming/before/functional-programming.md index 96df208..e0eac8f 100644 --- a/things-i-learnt/src/programming/before/functional-programming.md +++ b/things-i-learnt/src/programming/before/functional-programming.md @@ -1 +1,58 @@ # Learn The Basics of Functional Programming + +At this point, you should at least have heard about how cool functional +programming is. There are a lot of concepts here, but at least the very basic +ones you should keep in mind. + +A lot of talks about functional programming come with weird words like +"functors" and "monads". It doesn't hurt to know what they really mean +(disclaimer: I still don't). But some other stuff coming from functional +programming is actually easy to understand and grasp. + +For example, immutability. This means that all your data can't change once +it's created. Do you have a record with user information and the user changed +their password? No, do not change the password field, create a new user record +with the updated password and discard the old one. Sure, it does a lot of +"create and destroy" sequences which makes absolutely no sense (why would you +allocate memory for a new user, copy everything from the old one to the new +one, update one field, and "deallocate" the memory from the old one? It makes +no sense!) but, in the long run, it would prevent weird results, specially +when you understand and start use threads. + +(Basically, you're avoiding a shared state -- the memory -- between parts of +your code.) + +Another useful concept is pure functions. Pure functions are functions that, +called with the same parameters, always return the same result, no matter how +many times you call them. One example of a _non_ pure function is `random()`: +each time you call `random()`, you get a different number[^1]. An example of a +pure function would be something like this in Python: + +```python +def mult(x): + return x * 4 +``` + +No matter how many times you call `mult(2)`, it will always return 8. Another +example could be our immutable password change above: You could easily write a +function that receives a user record and returns a new user record with the +password changed. You could call with the same record over and over again and +it will always return the same resulting record. + +Pure functions are useful 'cause they are, first most, easy to test. + +Second, they are easy to chain, specially in a [data +flow](./data-flow) design: Because they don't have an +internal state (which is the real reason they are called pure functions), you +can easily call one after the other and no matter how many times you pass +things around, they still produce the same result. And because each function, +given the same input, produce the same result, chaining them all _also_ +produces the same result given the same inputs. + +Just those two concepts can make code longer (again, you're creating a new +user record instead of simply changing one field), but the final result is a +more robust code. + +[^1]: Except in Haskell, but it does require sending the seed every time, so + you end up with random values based on the seed, so even there it is a pure + function. diff --git a/things-i-learnt/src/programming/before/magical-number-seven.md b/things-i-learnt/src/programming/before/magical-number-seven.md index a1bc8e5..382aee0 100644 --- a/things-i-learnt/src/programming/before/magical-number-seven.md +++ b/things-i-learnt/src/programming/before/magical-number-seven.md @@ -1 +1,99 @@ # The Magic Number Seven, Plus Or Minus Two + +"[The magical number](https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two)" +is a psychology article about the number of things one can keep in their mind +at the same time. + +I've seen twice this weird construction on where a function would do some +processing, but its return value was the return of this processing, plus the +result of a second function and some bit of processing. Nothing major. But the +second function would also do some processing and call a third function. And +the third function would call a fourth. And the fourth a fifth. And the fifth, +a sixth function. + +And the "processing" was not something small, like "add two" or "multiply by +itself or a configurable value". + +Something like this + +``` +func_1 + +-- func_2 + +-- func_3 + +-- func_4 + +-- func_5 + +-- func6 +``` + +(If you need the _real_ processing I saw, it was a class that had a function +with some processing and then it would call a function in an injected +dependency -- which is pretty nice and dandy. But the injected dependency had +an injected dependency, and the third injected dependency _also_ had an +injected dependency, and so forth). + +Now, when you're trying to understand this kind of code to find a problem, +you'll have to keep in mind what the first, second, third, fourth, fifth and +sixth functions do, 'cause they are all calling each other (inside them). + +This causes some serious mental overflow that shouldn't be necessary. + +Not only that, but imagine that you put a log before and after `func_1`: The +log before points the data that's being send to `func_1`, and the log after +its result. + +So you'd end up with the impression that `func_1` does a lot of stuff, when it +actually is passing the transformation along. + +(I got a weird experience with a function called `expand`, which logging +before the call would show some raw, compressed data, but the after was not +the expanded data, but actually a list of already processed data from the +compressed data.) + +What would be a better solution, you may ask? + +Well, if instead of making `func_1` call `func_2`, you can make it return the +result (which may not be the final result, anyway) and _then_ call `func_2` +with that result. + +Something like: + +``` +result1 = func_1 +result2 = func_2(result1) +result3 = func_3(result2) +result4 = func_4(result3) +result5 = func_5(result4) +result6 = func_6(result5) +result7 = func_7(result6) +``` + +(If we go back to the dependency injection chain, you may think that instead +of making DI7 receive DI6 as dependency [which would receive DI5 as +dependency, which would receive DI4 as dependency, which would receive DI3 as +dependency and so forth] you would actually create all the DI (dependency +injections) in one single pass and then the starting function would call the +dependencies in a single shot, not chaining them.) + +Now you can see _exactly_ how the data is being transformed -- and, obviously, +the functions would have better names, like `expand`, `break_lines`, +`name_fields` and so on, so you can see that that compressed data I mentioned +before is actually being decompressed, the content is being broke line by +line, the lines are getting names in its fields and so on (and one could even +claim that it would make things clear if there was a function after +`break_lines` which would just `break_fields`, which would make `name_fields` +more obvious -- and in a construction like this it would be almost trivial to +add this additional step). + +"But that isn't performant!" someone may cry. Well, maybe it's just a bit less +performant than the original chained-calls ('cause it wouldn't create and +destroy frames in the stack, it would just pile them up and then "unstack" them +all in the end), but heck, optimization is for compilers, not people. Your job +is to make the code _readable_ and _understandable_. If you need performance, +you can think of a better sequence of steps, not some "let's make this a mess +to read" solution. + +Just a quick note: Although the famous paper mentions that the number is +around 7, new research is actually pointing that the number is way lower than +that, at 4. So simply making `func_1` call `func_2`, which would call +`func_3`, which would call `func_4` may be enough to overload someone and make +them lose the track of what the code does. diff --git a/things-i-learnt/src/programming/before/understand-shortcuts.md b/things-i-learnt/src/programming/before/understand-shortcuts.md index 7b25c4c..b6c90f2 100644 --- a/things-i-learnt/src/programming/before/understand-shortcuts.md +++ b/things-i-learnt/src/programming/before/understand-shortcuts.md @@ -1 +1,30 @@ # Shortcuts Are Nice, But Only In The Short Run + +A lot of languages/libraries/frameworks add a way to make things shorter, +reducing the number of things you need to type. + +But, later, that will bite you and you'll have to remove the shortcut and do +the long things. + +Frameworks and libraries -- and even some languages -- come with "helpers" for +most boilerplate things. Instead of typing the same 5 lines of code over and +over, you can use a simple function; instead of writing the function with 5 +parameters, you can skip a bit and use another one with just one. Or you could +just add a simple macro expansion on top of your struct/class and it would +complete all the missing points. + +Don't get me wrong, they are great. + +But you must understand what the macro/function is hiding from you. 'Cause +sooner or later, you'll find a case where it doesn't have a perfect fit and +you need to change just a small detail. And then you'll start running in +circles 'cause, well, how the hell the macro/function did _that_? + +I've bitten before by [Spring](http://spring.io/) and +[Serde](https://serde.rs/) 'cause I started with the shortcuts without +understanding what they were doing. And then I got a problem which the +shortcut wouldn't solve, requiring me to go deep into the documentation. And +because I skipped a few steps and jumped straight into the shortcut, it took +me awhile to actually get _what_ I needed to do different from the shortcut to +solve my problem: I had no idea what the shortcut did and, thus, I had no idea +what I needed differently from the shortcut to solve my problem. diff --git a/things-i-learnt/src/programming/before/users.md b/things-i-learnt/src/programming/before/users.md index c64af66..c62ef4c 100644 --- a/things-i-learnt/src/programming/before/users.md +++ b/things-i-learnt/src/programming/before/users.md @@ -1 +1,25 @@ # Think About The Users + +Think how the data you're collecting from your users will be used -- this is +more prevalent on these days, where "privacy" is a premium. + +I once had a discussion with a CTO about collecting the user IMEI on our +mobile app. Basically, there was no use case for capturing that information +yet but, as he put at the time, "We may want to know if one user uses two +phones, or if two users use the same phone". I raised the fact that we didn't +need this information and, besides that, it felt like we were invading the +users privacy. He still decided to go ahead. My answer: "I'll do it, but I +want to point that I'm not happy with it." + +In the end, the store blocked the app... because we were capturing the IMEI. + +But there are cases and cases. If you really _really_ need to capture user +information, be sure to protect it against unauthorized use, be it by external +forces (someone found a way to attack your data) or internal (some disgruntled +colleague decided to take the data from your users with them). + +And be sure, there _will_ be a leak at some point, it's just a matter of time. +If you can, the best way to protect your users data is to never capture it. +When a flaw on your system is found or when some colleague leaves the company +in bad terms, there will be no data to expose to the world, anyway. You can't +be more secure than this. diff --git a/things-i-learnt/src/programming/coding.md b/things-i-learnt/src/programming/coding.md index 819e264..802ed0a 100644 --- a/things-i-learnt/src/programming/coding.md +++ b/things-i-learnt/src/programming/coding.md @@ -1 +1,3 @@ # Writing code + +Let's put those things to work! diff --git a/things-i-learnt/src/programming/coding/boolean-parameters.md b/things-i-learnt/src/programming/coding/boolean-parameters.md index 4514bee..63195e9 100644 --- a/things-i-learnt/src/programming/coding/boolean-parameters.md +++ b/things-i-learnt/src/programming/coding/boolean-parameters.md @@ -1 +1,22 @@ # Don't Use Booleans As Parameters + +When you're designing a function, you may be tempted to add a flag (a +parameter in a function that it is a boolean). Don't do this. + +Here, let me show you an example: Suppose you have a messaging system and you +have a function that returns all the messages to an user, called +`getUserMessages`. But there is a case where you need to return a summary of +each message (say, the first paragraph) or the full message. So you add a +flag/Boolean parameter called `retrieveFullMessage`. + +Again, don't do that. + +'Cause anyone reading your code will see `getUserMessage(userId, true)` and +wonder what the heck that `true` means. + +You can either rename the function to `getUserMessageSummaries` and have +another `getUserMessagesFull` or something around those lines, but each +function just call the original `getUserMessage` with true or false -- but the +interface to the outside of your class/module will still be clear. + +But _don't_ add flags/Boolean parameters to your API. diff --git a/things-i-learnt/src/programming/coding/crash-it.md b/things-i-learnt/src/programming/coding/crash-it.md index dcd649d..2de7437 100644 --- a/things-i-learnt/src/programming/coding/crash-it.md +++ b/things-i-learnt/src/programming/coding/crash-it.md @@ -1 +1,41 @@ # It's Better To Let The Application Crash Than Do Nothing + +Although that sounds weird, it's better to not add any error handling than +silently capturing errors and doing nothing. + +For example, a (sadly common) example of Java code: + +```java +try { + something_that_can_raise_exception() +} catch (Exception ex) { + System.out.println(ex); +} +``` + +This does nothing to deal with the exception -- besides printing it, that is. + +The example may be a bit bad, 'cause Java forces capturing exceptions on +functions that throw exceptions and it forces functions to mark themselves as +throwing exceptions if there a `throw` in them. + +But Python doesn't have this restriction and people _still_ try to capture +exceptions for doing absolutely nothing -- and, worse, just keep the execution +going. + +If the language allows it, you should let the application crash due the lack +of error handling -- as long as you don't have any idea on how to handle it. +Then, when they crash, you can think of a way to deal with it, instead of +silently capturing it and doing nothing. + +Also, keep in mind to not go forth and capture _every_ exception/error in a +single take -- like the example above, which will capture every exception, or +like `except Exception` in Python. This last example actually happened to me +when another developer added this "broad except"[^1] in a network code and, at +some point, the code would get into the capture all the time. We checked every +cable, every connection, every interface, till I noticed there was a syntax +error in the code. In Python, syntax errors raise exceptions and, because we +had a "capture all exceptions", we lost some pretty good time looking for the +problem in the wrong place. + +[^1]: As called by Pylint. diff --git a/things-i-learnt/src/programming/coding/data-types.md b/things-i-learnt/src/programming/coding/data-types.md index 29fcf88..1601515 100644 --- a/things-i-learnt/src/programming/coding/data-types.md +++ b/things-i-learnt/src/programming/coding/data-types.md @@ -1 +1,31 @@ # Types Say What Your Data Is + +Memory is just a sequence of bytes; bytes are just numbers from 0 to 255; what +those numbers mean is described on the language type system. + +For example, in C, a `char` type of value 65 is most probably the letter "A", +which an `int` of value is 65 is the number 65. + +Remember this when dealing with your data. + +And it doesn't matter of your language of choice uses dynamic typing or static +typing. The same still applies. + +One classic example of misusing types is adding booleans. Booleans are either +`true` or `false`, but because most languages follow C, which doesn't have a +boolean type and uses compiler pre-processors to define `TRUE` as an integer +with the value `1` and `FALSE` with another integer with the value `0`. Newer +languages were build on top of what older developers knew, and so, a bunch of +those languages also assumed using an integer under booleans was a good idea. +And even today, with modern languages, people rely on those old methods. + +Let me repeat that: You're adding booleans and expecting a number -- only +because in the old times there wasn't boolean types. + +No, you're counting the number of elements in the list 'cause that would see +the whole list. You're not even filtering the false values over and counting +the resulting list size. You're jumping the underlying type to get a bit of +performance out. + +Fortunately, some new languages are using booleans as a complete different +type and wouldn't allow this kind of stuff. diff --git a/things-i-learnt/src/programming/coding/future-trashing.md b/things-i-learnt/src/programming/coding/future-trashing.md index 353760d..7043e0b 100644 --- a/things-i-learnt/src/programming/coding/future-trashing.md +++ b/things-i-learnt/src/programming/coding/future-trashing.md @@ -1 +1,19 @@ # Future Thinking Is Future Trashing + +When developers try to solve a problem, they sometimes try to find a way that +will solve all the problems, including the ones that may appear in the future. + +Trying to solve the problems that will appear in the future comes with a hefty +tax: future problems future will never come -- and, believe me, they will +_never_ come -- and you'll end up either having to maintain a huge behemoth of +code that will never be fully used or you'll end up rewriting the whole thing +'cause there is a shitton of unused stuff. + +Solve the problem you have right now. Then solve the next one. And the next +one. At one point, you'll realize there is a pattern emerging from those +solutions and _then_ you'll find your "solve everything". This pattern is the +_abstraction_ you're looking for and _then_ you'll be able to solve it in a +simple way. + +As Steve Jobs once said "You can't connect the dots looking forward, only +backwards". diff --git a/things-i-learnt/src/programming/coding/handle-it.md b/things-i-learnt/src/programming/coding/handle-it.md index 2b5df2d..9b87a1f 100644 --- a/things-i-learnt/src/programming/coding/handle-it.md +++ b/things-i-learnt/src/programming/coding/handle-it.md @@ -1 +1,27 @@ # If You Know How To Handle It, Handle It + +If you know an error can occur, then you should handle it properly, instead of +ignoring it. + +This is the opposite point of [let it crash](./crash-it.md): +You're writing some code that you _know_ it can crash in a certain way, what +should you do? Well, the answer is simple: _handle_ it, not _ignore_ it. + +If we go back to the fact that Java will describe every single exception that +can be thrown by a function, you should handle each exception, no excuses. + +If you're using Python, then you should capture the exceptions you know how to +handle, no exceptions -- and tying with the previous point, if you don't know +how to handle them, you should not capture them in the first place. + +But, no matter what language you're using, if you know an error/exception can +occur, _deal with it_. If you have to save the content of the user somewhere +else, log it to be reprocessed later or even just show an error message, do +it. + +Although I seriously meant it, it doesn't mean you have to remember every +single exception/error code and what it means when calling a function. You can +write code that will actually go through the happy path and later fill the +blanks. Or even, when you're working on another part of the code, if you +remember another problem, just write on a post-it and add the handling later. +The important bit is not to forget to handle it. diff --git a/things-i-learnt/src/programming/coding/interface-changes.md b/things-i-learnt/src/programming/coding/interface-changes.md index 2208896..6eb95d1 100644 --- a/things-i-learnt/src/programming/coding/interface-changes.md +++ b/things-i-learnt/src/programming/coding/interface-changes.md @@ -1 +1,24 @@ # Beware of Interface Changes + +Interfaces and APIs is what you give away to others. If you keep changing them, +you'll make everyone's life sad. + +When talking about [boolean parameters](./boolean-parameters.md), I mentioned +about renaming the function. If you control the whole source where the +function is used, that's not issue, it's just a matter of search and replace. + +But if that function was actually exposed in a library, you shouldn't change +function names in a whim. That will break a lot of other applications beyond +your control and make a lot of other people unhappy. + +Remember, when you write [tests for APIs](../testing/tests-apis.md), +you can see these kind of changes happening and you can see the kind of +changes you're doing on how they reflect externally. + +You can create the new functions and mark the current one as deprecated, +either by documentation or by some code feature. Then, after a few releases, +you can finally kill the original function. + +(A dickish move you can do is to create the new functions, mark the current +function as deprecated and _add a sleep at the start of the function_, in a +way that people using the old function are forced to update.) diff --git a/things-i-learnt/src/programming/coding/optimization.md b/things-i-learnt/src/programming/coding/optimization.md index ab7d49d..c0d4580 100644 --- a/things-i-learnt/src/programming/coding/optimization.md +++ b/things-i-learnt/src/programming/coding/optimization.md @@ -1 +1,28 @@ # Optimization Is For Compilers + +Let say you need more performance on your application. You may be tempted to +look at your code and think "How can I keep this same logic and still remove a +few cycles, so things seem to go faster?" Well, if you want performance, you +need to change your logic. + +But before jumping into the code, you may have to check your compiler options. +Maybe you're not generating the optimized version. Maybe there is an option +that you don't use that you can remove from the compilation. + +'Cause "optimization" is what a compiler is for. They _know_ where they can +extract most of the underlying architecture, and people have been finding ways +to make the compiled code more performance for decades. Heck, compilers can +even _delete_ parts of your code 'cause they can "see" that a piece of code +will always produce the same result and, thus, isn't necessary and they will +just put the same result where that piece of code was. + +What you need to do is to think about a better _design_ for your code, not how +to improve the current code. And trying to trick the compiler by [messing with +the types](./data-types.md), although may produce faster +code, will really screw you in the future -- specially cause maintenance and +code understanding will take long and figuring out what went wrong will always +be harder. + +Code is written for humans to read. _ALWAYS_. Optimization is what compilers +do. So find a smarter way to explain what you're trying to do instead of using +shorter words or messing with that your code is saying. diff --git a/things-i-learnt/src/programming/coding/outside-project.md b/things-i-learnt/src/programming/coding/outside-project.md index 13f2af4..3d503fa 100644 --- a/things-i-learnt/src/programming/coding/outside-project.md +++ b/things-i-learnt/src/programming/coding/outside-project.md @@ -1 +1,33 @@ # Don't Mess With Things Outside Your Project + +Simple rule: Is the code yours or from your team? Good, you can make any +changes you want. Does it come from outside? DON'T. TOUCH. IT. + +Sometimes people are tempted to, instead of using the proper extension tools, +change external libraries/frameworks -- for example, making changes directly +into WordPress or Django. Believe me, I've seen my fair share of this kind of +stuff going around. + +This is an easy way to make the project -- the team project, that is -- +a huge security problem. As soon as a new version is released, you'll -- or, +better yet, someone who was not the person who decided to mess with outside +code -- have to keep up your changes in sync with the main project and, pretty +soon, you'll find that the changes don't apply anymore and you'll leave the +external project in an old version, full of security bugs. + +Not only you'd end up with something that may very soon put at risk your whole +infrastructure, you won't take any benefits from things in the new versions, +'cause hey, you're stuck in the broken version! + +Sometimes doing it so is faster and cheaper, and if you would do the same +thing using extensions or actually coding around the problem, even duplicating +the framework functions, would probably take longer and make you write more +code, but in the long run, it's worth the time. + +Sometimes the change you need is impossible 'cause the framework you're using +doesn't have any support for extensions. This is the time you'll need to build +a new layer _on top_ of the framework. Again, this may seem painful and +changing the framework directly is a lot easier, but you'll have to keep +updating your patch for newer versions, which may not be that easy. Building +on top of the framework will at least give you some assurance 'cause the +exposed API must be way more stable than the internal code. diff --git a/things-i-learnt/src/programming/coding/permanent-solution.md b/things-i-learnt/src/programming/coding/permanent-solution.md index 9612b47..1383615 100644 --- a/things-i-learnt/src/programming/coding/permanent-solution.md +++ b/things-i-learnt/src/programming/coding/permanent-solution.md @@ -1 +1,31 @@ # Nothing More Permanent Than A Temporary Solution + +Depending on where you look, "Nothing more permanent than a temporary +solution" is either an old Russian proverb or a quote by Milton Friedman. +Thing is, temporary solutions, unless you think about the future to fix them, +will become permanent. + +A temporary solution may appear either as a proof-of-concept or due some +restrained deadline. You may create perfect [system +specs](../before/spec-first.md), you may have a perfect +understanding of the whole [in your Gherkin +files](../before/gherkin.md) but, at some point, you'll put some +small script to fix a minor problem, or do a "not so good" solution to a point +due to deadlines. + +This happens and unless you take steps to get rid of those, you'll end up with +a bunch of spaghetti code pretty fast. And that will snowball to a point that +you won't be able to manage the project. + +Once a scrum master suggested that we came with an idea to our product manager +to do something akin to "Every three sprints, we'll focus on product value; +the fourth one is ours to fix the things that are annoying us". I don't think +we ever talking to the product manager about this, but we managed to open +issues on our ticket system about the small warts we left behind, specially +due deadlines. So there we had, a specially crafted bug type for "technical +debt" which we never actually took the time to fix. + +Unless you have a pretty good safety net to fix those, they will life forever. +And it may be a better option to tell "we can't deliver in time" than adding +(yet another) temporary solution, as hard as it is to convince the higher ups +that you can't deliver the product with a temporary solution. diff --git a/things-i-learnt/src/programming/coding/resist-easy.md b/things-i-learnt/src/programming/coding/resist-easy.md index 6993cd0..a22eea8 100644 --- a/things-i-learnt/src/programming/coding/resist-easy.md +++ b/things-i-learnt/src/programming/coding/resist-easy.md @@ -1 +1,21 @@ # Resist The Temptation Of Easy + +Sure that IDE will help you with a ton of autocomplete stuff and let you +easily build your project, but do you understand what's going on? + +I'm not denying the fact that IDEs make things easier. I'm trying to say that +you should not rely heavily on their features. + +I mentioned before that you should at least know how to [run tests on the +command line](../testing/tests-in-the-command-line.md) and the same +applies to everything in IDEs: how to build, how to run, how to run tests and, +let's be honest here, how to find proper names for your variables and +functions. 'Cause it's nice that the IDE can complete all the names of +the functions, but if the autocomplete feature was off, would you know which +function you need? In other words, have you thought at least 10 seconds about +a good name for your function so you _won't_ need to use autocomplete to +remember its name? + +These days, IDEs can autocomplete almost everything, from function names to +even how to name your variables. But using the autocomplete is not always a +good solution. Finding better names is. diff --git a/things-i-learnt/src/programming/coding/run-locally.md b/things-i-learnt/src/programming/coding/run-locally.md index c0aa3d4..7ff1ffc 100644 --- a/things-i-learnt/src/programming/coding/run-locally.md +++ b/things-i-learnt/src/programming/coding/run-locally.md @@ -1 +1,32 @@ # If It Doesn't Run On Your Computer, You Have A Problem + +I've seen a lot of systems that would never run on a isolated computer, like +the developer tool, 'cause the system requires running on a specialized +environment. Those things are wrong. + +Requiring a specialized environment absolutely kills productivity. + +If your system will run on a specialized environment -- and I'm including "the +cloud" here -- look for something that can abstract whatever you're using. For +example, if you're using AWS SQS, which is a queue, look for a library that +can abstract the way a queue works so you can also run with RabbitMQ, which +can be easily run on your own computer. + +If you're using a very specialized thing, you may have to write the +abstraction yourself, isolating it from the main system, so you can develop +the main product in peace. + +One of the most productivity killer environment I worked require running the +project on a customized Apache installation, running the client specialized +framework. The whole problem is that the client refused to allow us to not use +it or install on our local machines (mostly 'cause the install of said +framework was really complex). In other for us to work and see things working, +we had to use a VPN to the client computers, develop things there and manually +forcing things to reload. No only we had absolutely nothing to do when the VPN +was down ('cause it require out company infrastructure working hand-in-hand +with the client infrastructure and the internet provider infrastructure, which +is absolutely impossible), the flow was really cumbersome. + +If we had the chance to not use it and run all the development and tests on +our own computers, I have the feeling we could deliver the product 2-3 months +earlier. diff --git a/things-i-learnt/src/programming/coding/start-stupid.md b/things-i-learnt/src/programming/coding/start-stupid.md index 8fdcda7..0d18fe5 100644 --- a/things-i-learnt/src/programming/coding/start-stupid.md +++ b/things-i-learnt/src/programming/coding/start-stupid.md @@ -1 +1,24 @@ # Start Stupid + +One way to get away from the IDE is to "start stupid": Just get the compiler +and get an editor (ANY editor) with code highlight and do your thing: Code, +build it, run it. + +Notice that say "stupid way", not "simple way". + +Doing things in the stupid way is not the easiest way to start a project. How +could one beat the easy of clicking a button and having the whole structure of +a project done for you? + +But starting it in the stupid way, in which you have to think your project +layout, how to build stuff, how to run tests, how to do _everything_ may give +you some insights on how things work, how the pieces mesh together and how to +cogs turn around. Even better: It make give you some insights on what +_doesn't_ work. + +Honestly, you don't have to do this with all projects. You can still use your +favourite IDE and do things in the easy way. But you can also have that side +project on which you'll do everything in the stupid way, just to understand +what your IDE is doing. + +And when you grasp that, you'll be able to use _any_ IDE. diff --git a/things-i-learnt/src/programming/coding/throw-away.md b/things-i-learnt/src/programming/coding/throw-away.md index 707f670..e1086d7 100644 --- a/things-i-learnt/src/programming/coding/throw-away.md +++ b/things-i-learnt/src/programming/coding/throw-away.md @@ -1 +1,32 @@ # Be Ready To Throw Your Code Away + +A lot of people, when they start with TDD, get annoyed when you say that you +may have to rewrite a lot of stuff, including whatever your already wrote. + +TDD was _designed_ to throw code away: The more you learn about your problem, +the more you understand that, whatever you wrote, won't solve the problem in +the long run. Also, as you slowly solve new problems, you may notice some +pattern in the code emerging (you're doing the same thing over and over, with +only minor changes). That's a good time to go over and rewrite everything to +take advantage of this pattern. + +You shouldn't worry about this. Your code is not a wall (or any physical +object): if you have to throw it away, you didn't wasted materials. Surely it +means your time writing code was lost, but you got a better understanding +about the problem now, or you may start to think in a more concise way to +solve the problem. + +Not only that, but as you progress through your project, solving problems and +getting "acquainted" with the problem, you'll also notice that the +[spec](../before/spec-first.md) will also change. This means that +the problem your code solve wasn't exactly the problem you _needed_ to solve; +your code is trying to solve something that isn't exactly the problem. + +Also, specs changing is really common. One thing that you can be sure is that +it won't change _everywhere_. Some of the things you solved will stay the +same, some others will be completely removed and some others added. And you +will see that you'll refactor your code a lot, and throw a lot of code away. +And not just code that solves the problem, but also the tests for that code. + +... unless you focus mostly on [integration +tests](../testing/integration-tests.md). diff --git a/things-i-learnt/src/programming/coding/units.md b/things-i-learnt/src/programming/coding/units.md index 5a5ea10..413cb76 100644 --- a/things-i-learnt/src/programming/coding/units.md +++ b/things-i-learnt/src/programming/coding/units.md @@ -1 +1,19 @@ # Units Makes Things Clear + +You know what's one of the worst function names ever? `sleep()`. + +Sleep for how long? It is seconds or milliseconds? + +Now let me ask you this: Would it clearer if the function was called +`sleepForMs()`? Would you understand that the function would make the +application sleep for a number of milliseconds? + +What about `sleepForSecs()`? Do you understand that this will force your +application to sleep for a number of seconds? + +What if, instead of using the function name, you could use `sleep("10s")`? Does +it make clear that you want it to sleep for 10 seconds? + +That's why adding units to the function or parameters make sense. It removes +the ambiguity of what it means and doesn't rely on some specialized IDE/Editor +that display the parameter names. diff --git a/things-i-learnt/src/programming/coding/use-structures.md b/things-i-learnt/src/programming/coding/use-structures.md index 8ac3601..9a7050d 100644 --- a/things-i-learnt/src/programming/coding/use-structures.md +++ b/things-i-learnt/src/programming/coding/use-structures.md @@ -1 +1,62 @@ # If Your Data Has a Schema, Use a Structure + +You may be tempted to use a list (or tuple, if your language allows) to keep +your data if it has, say, only 2 fields. Don't. + +Some languages allow unstructured data to be kept in the format of tuples: +They act like lists, but you can use to store heterogeneous data (which is a +cute way of "it stores fields of different types"). + +This languages also allow you to "destructurize" them, so you can extract +elements from them without directly accessing them by index. + +For example, in Python, you can have a tuple with: + +```python +a_tuple = ('A String', 1, 7.5) +``` + +And you can destructure it with + +```python +some_string, an_integer, a_float = a_tuple +``` + +See? It's simple! You don't need to create a whole structure if you're just +passing a string, an integer and a float around. + +Except, you do need a structure 'cause your data has a _schema_. + +Tuples and destructuring should be used only when you need to pass data from +one function to another -- and barely that. When you have this tuple being +passed around, being destructured and created again -- say, you are adding one +value of the tuple to another value and producing a new tuple in the same +format -- then you have a structured -- and _schemaed_ data. + +And when you have a structured data, you must use a data class or a struct (or +even +[NamedTuples](https://docs.python.org/3/library/collections.html?highlight=namedtuple#collections.namedtuple), +if you're using Python). + +Although it may look way more simpler to keep destructuring and building the +tuple over and over, in the long run you'll end up with a mess: a simple +change -- like adding a new field -- will require checking every destructuring +and every creation of the tuple to make sure if will stay in the same shape +every time. + +So: You data has a schema? Use a Data Class or Class or Struct. Only if it is +schemaless, then you can use a tuple. + +I've seen this used at least once. At the very start of the project, it +may seem easier to just store the data as a tuple and destructure it and build +it again when needed. There was even a whole module designed to receiving +tuples, destructure them and rebuild new ones (for example, a function that +would receive two tuples and compute the sum of the "value" field of each, +building a new tuple as a result). But because of this design, to add just a +new field, I had to change 14 files and do 168 changes around -- 'cause there +was a function to add two tuples, but there were points where you need just +one field, and there wasn't a function for it. + +It would be easier to use if there were functions to extract each field, and +add two tuples, and what else was needed for managing the tuples, but then you +have to ask yourself: Why not use a class for that? diff --git a/things-i-learnt/src/programming/coding/use-timezones.md b/things-i-learnt/src/programming/coding/use-timezones.md index 9dab464..2c3c211 100644 --- a/things-i-learnt/src/programming/coding/use-timezones.md +++ b/things-i-learnt/src/programming/coding/use-timezones.md @@ -1 +1,31 @@ # Always Use Timezones With Your Dates + +No matter if the date you're receiving is in your local timezone and you'll +display it in your timezone, sooner or later, the fact that you ignored there +was a timezone behind that date will hurt you. + +(Note: Most of this post when I say "date" you can think of "date and time", +although the date should also be "timezone aware".) + +At some point of my professional life, ignoring timezones was easy: You just +pick the date, throw in the database, then read it back and everybody was +happy. + +But things are not like this anymore. People will access your site from far +away locations, the source of the date may not be in the same timezone of your +system, your system may be running in a completely different timezone of your +dev machine (it's pretty common to run things in our machines in the local +timezone but the production system will run in UTC), the display may be a +complete different timezone than your production and dev machine and so on. + +So always carry the timezone with the data. Find modules/classes that support +dates with timezones (a.k.a. make things _timezone aware_), capture the +timezone as soon as possible and carry it around in all operations. +Modules/classes that don't support timezones for dates/times should, as soon +as possible, removed from the system. + +Any developers a bit more seasoned -- and by "seasoned" I meant "Had to deal +with times before" -- will probably claim "Hey, this is _obvious_!" And I'd +have to agree. But it's annoying how many times I got bitten by some stupid +bug 'cause we decided that "well, everything is in the same timezone, so it's +all good". diff --git a/things-i-learnt/src/programming/coding/use-utf8.md b/things-i-learnt/src/programming/coding/use-utf8.md index 894599b..aa8c956 100644 --- a/things-i-learnt/src/programming/coding/use-utf8.md +++ b/things-i-learnt/src/programming/coding/use-utf8.md @@ -1 +1,45 @@ # Always Use UTF-8 For Your Strings + +Long gone are the days where [ASCII](https://en.wikipedia.org/wiki/ASCII) was +enough for everyone. Long gone are the days where you can deal with strings +with no "weird" or "funny" characters. + +I became a developer in a time when the only encoding we had was ASCII. You +could encode all strings in sequences of bytes, 'cause all characters you +could use where encoded from 1 to 255 (well, from 32 [space] to 93 [close +brackets] and you still have a few latin-accented characters in some higher +positions, although not all accents where there). + +Today, accepting characters beyond that is not the exception, but the norm. To +cope with all that, we have things like +[Unicode](https://en.wikipedia.org/wiki/Unicode) and +[uTF-8](https://en.wikipedia.org/wiki/UTF-8) for encoding that in reasonable +memory space (UTF-16 is also a good option here, but that would depend on your +language). + +So, as much as you to make your system simple, you will have to keep the +internal representation of your strings in UTF-8/UTF-16. You may not receive +the data as UTF-8/UTF-16, but you'll have to encode it and keep transmitting +it around as UTF-8/UTF-16 till you have to display it, at which point you'll +convert from UTF-8/UTF-16 to whatever your display supports (maybe it even +supports displaying in UTF-8/UTF-16, so you're good already). + +Today, I believe most languages do support UTF-8, which is great. You +may still have problems with inputs coming from other systems that are not +UTF-8 (old Windows versions, for example), but that's fairly easy to convert +-- the hard part is figuring out the input _encoding_, though. Also, most +developers tend to ignore this and assume the input is in ASCII, or ignore the +input encoding and get a bunch of weird characters on their printing, +'cause they completely ignored the conversion on the output point. That's why +I'm repeating the mantra of UTF-8: To remind you to always capture your input, +encode it in UTF-8 and _then_ convert in the output. + +One thing to keep in mind is that UTF-8 is not a "cost free" encoding as +ASCII: While in ASCII to move to the 10th character, you'd just jump 10 bytes +from the start of the string, with UTF-8 you can't, due some characters being +encoded as two or more bytes (you should read the Wikipedia page; the encoding +is pretty simple and makes a lot of sense) and, due this, you can't simply +jump 10 characters 'cause you may end up in second byte that represents a +single character. Walking through the whole string would require traversing +the string character by character, instead of simply jumping straight to the +proper position. But that's a price worth paying, in the long run. diff --git a/things-i-learnt/src/programming/documentation.md b/things-i-learnt/src/programming/documentation.md index 5f1f07b..b835a7e 100644 --- a/things-i-learnt/src/programming/documentation.md +++ b/things-i-learnt/src/programming/documentation.md @@ -1 +1,3 @@ # Documenting your code + +What does this piece of code do? Is "self-documenting code" actually real? diff --git a/things-i-learnt/src/programming/documentation/document-and.md b/things-i-learnt/src/programming/documentation/document-and.md index 199eda7..7e4573c 100644 --- a/things-i-learnt/src/programming/documentation/document-and.md +++ b/things-i-learnt/src/programming/documentation/document-and.md @@ -1 +1,23 @@ # If A Function Description Includes An "And", It's Wrong + +Functions should do one thing and one thing only. I clear indication that +you're breaking this principle is the need to add an "and" in its +documentation. + +This is kind like "sometimes rule", but most of the time, when you feel you +need to add an "and" to the function documentation (its +[contract](./document-is-contract.md)), then you're telling +that that function is doing two (or more) things. + +One of guiding principles of good code is the [Single responsibility +principle](https://en.wikipedia.org/wiki/Single_responsibility_principle), in +which each module/class/function should do one thing and one thing only. And, +again, if you're saying that a function is doing "this" _and_ "that", you can +be sure it's not doing just _one_ thing. + +Ok, but what now? Well, you have two functions, with two distinct contracts. +Ok, but you _had_ those two being called, what happens now? Well, where you +called one, you now will need to call two. If your preferred language have +support for function composition, you can use that to group both functions +again. This is the kind of stuff that you'll get when you [learn to use +functional programming](../programming/functional-programming.md). diff --git a/things-i-learnt/src/programming/documentation/document-is-contract.md b/things-i-learnt/src/programming/documentation/document-is-contract.md index 62b0f7b..590185a 100644 --- a/things-i-learnt/src/programming/documentation/document-is-contract.md +++ b/things-i-learnt/src/programming/documentation/document-is-contract.md @@ -1 +1,29 @@ # The Function Documentation Is Its Contract + +When you start the code by [writing the general flow as +steps](../programming/steps-as-comments.md) and making each step a +function, you're actually making a contract (probably with your future self): +I'm saying this function does _this_ and _this_ is what it does. + + + +Remember that the documentation must be a clear explanation of what your code +_is_ doing and _why_ it exists; remember that good messages will make [reading +the code only by the function documentation](./document-id.md) should be +clear. + +A function called `mult`, documented as "Get the value and multiply by 2" but, +when you look at the code, it does multiply by 2, but also sends the result +through the network or even just asks a remote service to multiply the +incoming result by 2, is clearly breaking its contract. It's not just +multiplying by 2, it's doing more than just that, or it's asking someone else +to manipulate the value. + +Now, what happens when this kind of thing happens? + +The easy solution is to change the documentation. But do you know if people +who called the function expecting it to be "multiply value by 2" will be happy +for it to call an external service? There is a clear breach of "contract" -- +whatever you initially said your function would do -- so the correct solution +would be to add a new function with a proper contract -- and probably a better +name. diff --git a/things-i-learnt/src/programming/documentation/document-it.md b/things-i-learnt/src/programming/documentation/document-it.md index 24e464d..20fcf15 100644 --- a/things-i-learnt/src/programming/documentation/document-it.md +++ b/things-i-learnt/src/programming/documentation/document-it.md @@ -1 +1,37 @@ # Documentation Is A Love Letter To Your Future Self + +We all know writing the damn docs for functions and classes and modules is a +pain in the backside. But realizing what you were thinking when you wrote the +function will save your butt in the future. + +When I say that it will save your butt, I don't mean the documentation will +tell you something like "Here are the lotto numbers in 2027"[^1] or "If John +complains about your future code review, here is some shit he did in the +past". + +I mean, it will explain how the _flow_ of your code is expected to do. Imaging +this: pick your code and replace every function call to its documentation. Can +you understand what it is expected by reading that? If you can, +congratulations, you won't have a problem in the future; if you can't... well, +I have some bad news for you... + +One point that may come here is "Code is its own documentation" or +"self-documenting code". I do understand, and yes, simpler functions may make +the documentation redundant (for example, if you notice that you need a +function that multiplies two numbers -- and only do that -- giving it a +description of "Multiples two numbers" may look redundant), but you have to +ask yourself _why_ you needed such simple function. _Why_ it exists? _Where_ +it sits in the general data flow? + +Another thing you can document: rarely used functions. One example is Java +Collectors: In Java, you can create a stream of data, which you can apply +transformations and such and, in the end, you may put the resulting collection +of data into another structure -- a list, for example. The thing is, +collecting to a list is pretty common, but collecting into a map, with a +function as key and another value as value, splitting the result into two +different data blocks, is not that common. Because it is uncommon to see such +collector, it is a good idea to add tips on what each option is. + +That's the things you need to document. + +[^1]: Please, don't make me revise this in 2027... :( diff --git a/things-i-learnt/src/programming/documentation/languages-docs.md b/things-i-learnt/src/programming/documentation/languages-docs.md index f3e204d..7cdd4cf 100644 --- a/things-i-learnt/src/programming/documentation/languages-docs.md +++ b/things-i-learnt/src/programming/documentation/languages-docs.md @@ -1 +1,29 @@ # Good Languages Come With Integrated Documentation + +If you're worried about learning some new programming language, you can bet +the one with a better documentation is the one that is _born_ with a document +processor. + +Same goes for the frameworks/libraries of that language. + +The answer for that is the same as [languages that come with +tests](../testing/languages-tests.md): because the programming language +standard library comes with a documentation generator or even because +documentation is bundled in the language itself, it reduces the friction +needed to start writing the documentation. + +Python is a curious case that it came with a simple documentation generator +(PyDoc) and a bundled documentation format (DocStrings). Nowadays, almost +nobody is using the default documentation generator anymore, but because the +documentation format is still there and is still supported by the language +(documentation appears as a property of every function, class and module), +other tools took the post of default documentation generator, but the +documentation format is still heavy used. + +Also, the opposite seems almost always true: If the language doesn't come with +integrated documentation, there is a very good chance that the documentation +or the language or frameworks and libraries will be bad. Or, in the very +least, every library will pick its own format, every framework will pick its +own format and they will never match the language format, and you'll end up +with a mess of a documentation to decipher. + diff --git a/things-i-learnt/src/programming/organization.md b/things-i-learnt/src/programming/organization.md index a508d47..2423fb2 100644 --- a/things-i-learnt/src/programming/organization.md +++ b/things-i-learnt/src/programming/organization.md @@ -1 +1,4 @@ # Project Organization + +Everything is falling into place: You know how to code, you know what to +document, you know how to test... but how do you put everything together? diff --git a/things-i-learnt/src/programming/organization/libraries.md b/things-i-learnt/src/programming/organization/libraries.md index 12eea55..66f9082 100644 --- a/things-i-learnt/src/programming/organization/libraries.md +++ b/things-i-learnt/src/programming/organization/libraries.md @@ -1 +1,38 @@ # Create Libraries + +One thing you must learn is how to break your project into smaller libraries, +to avoid doing rounds to deal with "the same, but a bit different". + +I've seen a lot of projects that use things like branches for different +things. Say, you have an e-commerce page. But you also have different clients, +and they all have different colours and logo. Some people would take this +scenario and, using the VCS properties, use the main branch for the main code +and a branch for each client, merge from main branch from time to time -- and, +thus, the branches are never merged back. + +This is suboptimal, 'cause that's not how VCS are supposed to be used. + +But you can, for example, break the main code into a library/framework and +have one project for each client, with their assets and you just reference the +library/framework in each. + +Simple and clean. + +But stop there for a second. Although this makes the code cleaner, avoids +duplication and uses a VCS in the way it was supposed to be used, you can't +start this way. + +Remember that [future thinking is future +trashing](../programming/future-trashing.md). What you can do is actually +break your project by functionality, [making modules related to their +data](./project-organization.md) and then, when you get a reasonable number of +clients, you'll notice what can be reused in each, what modules make sense for +one client and not for another. And then you'll have a good way to deal with +those. + +One project that may appear when creating libraries is "How do I create my own +library repository?" 'Cause all modern languages today have support for +importing external libraries and, even if your libraries will never be out of +your control, they are external to the project. So you may need to learn how +to deal with this before creating the libraries. And, unfortunately, each +language and build tool has its own way to manage this. diff --git a/things-i-learnt/src/programming/organization/paper-notes.md b/things-i-learnt/src/programming/organization/paper-notes.md index b20513d..dbe59fd 100644 --- a/things-i-learnt/src/programming/organization/paper-notes.md +++ b/things-i-learnt/src/programming/organization/paper-notes.md @@ -1 +1,14 @@ # Paper Notes Are Actually Helpful + +I've tried to go paperless many, many times. But keeping a notepad and a bunch +of post its in my desk has been one of the most helpful tools I ever got. + +I've even managed to hide all my pens, move notepads to desks and use some +note-taking application instead. In the end, none of those managed to come +close to the utility of having something to scribble notes fast, or to draw a +very high concept of whatever I'm trying to explain. + +Also, a desk full of post its, or even a monitor with a bunch of things around +gives the impression that you're really busy working -- just be careful to not +have too many post its, or it will look like you can't complete anything. It +even beats Trello! diff --git a/things-i-learnt/src/programming/organization/project-organization.md b/things-i-learnt/src/programming/organization/project-organization.md index 22f60b0..e5b7575 100644 --- a/things-i-learnt/src/programming/organization/project-organization.md +++ b/things-i-learnt/src/programming/organization/project-organization.md @@ -1 +1,74 @@ # Organize Your Code by Data/Type, Not Functionality + +A lot of projects assume that you'll put things with the same functionality in +the same place, no matter what data they deal with. This makes things harder +to break apart later. + +Most projects keep organized by the functionality each component do. For +example, all the models are in the same place, all the functions that convert +one model into an internal structure/DTO are kept together, and so on. +Something like this: + +``` +. ++-- IncomingModels +| +-- DataTypeInterface +| +-- DataType1 +| +-- DataType2 +| +-- DataType3 ++-- Filters +| +-- FilterInterface +| +-- FilterValidDataType2 ++-- Processors +| +-- ProcessorInterface +| +-- ConvertDataType1ToDto1 +| +-- ConvertDataType2ToDto2 ++-- OutgoingModels + +-- DtoInterface + +-- Dto1 + +-- Dto2 +``` + +This is fine and works. But when you organize by data, it'll make a lot easier +to split your project in smaller projects -- 'cause, at some point, you may +want to do almost the same thing as you're doing right now, but with small +differences. + +``` +. ++-- Base +| +-- IncomingModels +| | +-- DataTypeInterface +| +-- Filters +| | +-- FilterInterface +| +-- Processors +| | +-- ProcessorInterface +| +-- OutgoingModels +| +-- DtoInterface ++-- Data1 +| +-- IncomingModels +| | +-- DataType1 +| +-- Processors +| | +-- ConvertDataType1ToDto1 +| +-- OutgoingModels +| +-- Dto1 +... +``` + +Now you can make a module that deals _only_ with Data1, another that works +only with Data2 and so on. And then you can break them into isolated modules. + +And then when you have another project that also have Data1 but also deals +with Data3, you can reuse most of the stuff in the Data1 module. + +And I do understand that this creates an explosion of directories/packages, +which may seem a bit unnecessary. + +Believe me, I also thought the idea of keeping things by functionality made +more sense. But in one project, I got a requirement to do almost the same +thing as I was doing before, but with a small change, which would require one +less step/transformation (in our example, you can think as the new requirement +as doing exactly what the Data1, Data2 and Data3 did, with their +transformations and such, but without the Data3 part). By breaking by their +types, I managed to create small modules for each one and the new project +would simply reference Data1 and Data2, but not Data3. diff --git a/things-i-learnt/src/programming/running.md b/things-i-learnt/src/programming/running.md index 4f19928..3042344 100644 --- a/things-i-learnt/src/programming/running.md +++ b/things-i-learnt/src/programming/running.md @@ -1 +1,4 @@ # Making Things Go + +How to make things easier for you when you already have the application in +order? diff --git a/things-i-learnt/src/programming/running/add-then-remove.md b/things-i-learnt/src/programming/running/add-then-remove.md index b6cbfe5..a6529e8 100644 --- a/things-i-learnt/src/programming/running/add-then-remove.md +++ b/things-i-learnt/src/programming/running/add-then-remove.md @@ -1 +1,23 @@ # One Version To Add, One Version To Remove + +A lot of things change during development. One day you need a field, another +day that field may be completely different. For those cases, use one version +to add the new field and another to remove. + +You have a database with a lot of customers and their ID is numerical. But for +some reason, they now need to be strings. Instead of changing the field type +and doing a whole migration, make a deploy with a new field, in which you'll +keep the old _and_ the new format going on and, in the next release, remove +the old field. No downtime. You can even run the migration while the system is +up, since the new field won't be used. + +(I'm simplifying the problem a lot here, 'cause the customer would have +references all around your system, but you get the point, right?) + +I had a problem in which we store the link for an object directly in the +backend (we shouldn't, that's a frontend problem, but that's a discussion for +another time); our interface is changing and so should the link. If we did a +change in the link directly, that would mean the backend would have to be +deployed _at the same time_ as the new interface; by adding the new link +format in another field, we can deploy the backend easily without breaking the +current system. diff --git a/things-i-learnt/src/programming/running/app-composition-stupid.md b/things-i-learnt/src/programming/running/app-composition-stupid.md index 4155abd..369ce3b 100644 --- a/things-i-learnt/src/programming/running/app-composition-stupid.md +++ b/things-i-learnt/src/programming/running/app-composition-stupid.md @@ -1 +1,20 @@ # Even for Application Composition, Start Stupid + +Application composition may lead to microservices -- which is good -- but +microservices require some ideas about how applications "talk" between them +over the wire (protocols and such) which you don't need to start with. + +Again, because you just want to simplify your work, you can make the +applications use files directly: Have your first application generate two +files and the second application receive the file names from [the command +line](./command-line-options.md). There, simple and stupid, +and works. + +You can even make the first application, instead of generating a file, just +send its result on the standard output, and have the second application +receive the data from the standard input -- both of which are managed as +files, anyway. Then, with a bit of magic, you can put everything together +without wasting space. + +Worry about talking over the wire later, when you understand how networks +work. diff --git a/things-i-learnt/src/programming/running/application-composition.md b/things-i-learnt/src/programming/running/application-composition.md index 7382f97..2ce2910 100644 --- a/things-i-learnt/src/programming/running/application-composition.md +++ b/things-i-learnt/src/programming/running/application-composition.md @@ -1 +1,34 @@ # Not Just Function Composition, But Application Composition + +When we were discussing [the magical number +seven](../before/magical-number-seven.md), I mentioned that it made +more sense to actually call the functions in sequence instead of each calling +the next. That's basically a "function composition", one thing you can also do +with your applications. + +Unix came with the idea of "applications that do one thing and do it well". +And then you could just pick the output of one application and plug it as +input of another (and then plug the output of the second into a third, and so +on). + +Also, I mentioned that you could use [configuration +files](./config-file.md) to do the same processing over +different source elements (based on a configuration, that is) instead of +writing an application that would process both in a single shot. + +One problem with that approach is that you may need _both_ results to actually +produce a usable result (for example, how would you build a list of common +followings of two Twitter users if you don't have both lists?). + +That problem can easily be solved if you write a different application that +just receives both lists and compare them. That would greatly simplify your +general codebase 'cause instead of one massive codebase with lots of moving +pieces, you'd have two small codebases, with less moving pieces. One could +still break the other -- say, if you or someone else changes the result of the +first function -- but you will still get the results of the first without +missing the whole 'cause the second is breaking. + +PS: I reckon it's really hard to create application composition with graphical +applications (why would you ask your user to have _two_ applications open at +the same time to make something work?) but you can extrapolate this for almost +everything else. diff --git a/things-i-learnt/src/programming/running/command-line-options.md b/things-i-learnt/src/programming/running/command-line-options.md index 70e74c6..27a4f12 100644 --- a/things-i-learnt/src/programming/running/command-line-options.md +++ b/things-i-learnt/src/programming/running/command-line-options.md @@ -1 +1,22 @@ # Command Line Options Are Weird, But Helpful + +In this day and age, when everything has a graphical interface, does it still +makes sense to add command line options to your application? In fact, it does. + +When I mentioned the configuration file, you may have thought about using +adding a default path for it and using the same file over and over. + +Well, that's not wrong, but what if you want to use a different configuration? +Would you keep moving the original configuration file to another place, moving +your configuration back and keep this back and forth? Keep both versions and +just use a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) with +the configuration filename pointing to the one you want? + +Why not add a command line option in which the user can select which +configuration file should be loaded? + +This would make their life _and yours_ easy. + +Also, be aware that, today, there may be libraries to handle command line in +every language, which will help you build a good command line interface, along +with standardizing it to have the same interface as other applications. diff --git a/things-i-learnt/src/programming/running/config-file.md b/things-i-learnt/src/programming/running/config-file.md index 66945fa..721051e 100644 --- a/things-i-learnt/src/programming/running/config-file.md +++ b/things-i-learnt/src/programming/running/config-file.md @@ -1 +1,45 @@ # The Config File Is Friend + +Do not ignore the power of configuration files. + +Imagine you wrote a function that you have to pass a value for it to start +processing (say, a twitter user account id). But then you have to do that with +two values and you just call the function again with the other value. + +It makes more sense to use a config file and just run the application twice +with two different config files 'cause, this way, you have a single, small, +testable application instead of two, or a very complex application that does a +lot of stuff. + +We can even jump into the idea of [creating +libraries](../programming/libraries.md) and say that, instead of +splitting your e-commerce application into smaller parts and making a big one +by grouping these smaller parts, you could simply have one e-commerce +application and, for each of your clients, you would have a different +configuration file, pointing to different assets. This way, even the assets +may reside in the same repository in the same branch, 'cause all that +identifies which assets should be used are defined in the configuration file. + +"But which one should I use?" you may ask. Well, "it depends". It may make +sense to have one single application with different configuration files if +most of its can be used all the time. If the intersection of used things is +very small, it may make more sense to split into different libraries and just +"pick and chose" what to use. + +But besides the replacement of libraries, you can also think things like: "Ok, +I have to remove elements after a while[^1]; but which would be a good time +that they can exist before I can remove them?" Well, if you're not quite sure +(and, sometimes, even when you're sure), you can use a configuration file to +define how long those elements will stay in the system before being expunged. +Maybe you're not even thinking about how long each element will stay in the +system, but how many of those elements you'll keep in the system before +removing the old ones -- which is, again, a good candidate to be moved to a +configuration file. + +Configuration files allow you to change properties of the system without +recompiling everything. And, if in the future you decide to follow the [12 +Factor app](https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology), +you'll find that you're half-way through it. + +[^1]: In other words, they have a [time to + live](https://en.wikipedia.org/wiki/Time_to_live). diff --git a/things-i-learnt/src/programming/running/log-events.md b/things-i-learnt/src/programming/running/log-events.md index 67437f5..f62cb75 100644 --- a/things-i-learnt/src/programming/running/log-events.md +++ b/things-i-learnt/src/programming/running/log-events.md @@ -1 +1,36 @@ # Logs Are For Events, Not User Interface + +Two things in one: First of all, when using logging, use it to log events, not +for user interfaces; second, log _events_ in a machine readable way, not +necessarily an human readable format. + +For a long time, I used logs to show to the user what was happening. To me, it +was logical to use something where I could mark errors as errors, general +information as information and, if the user requested more information, print +more information on what was going on. So I just added logging, defined normal +messages as `info`, errors as `errors`, information that may help me find +errors as `debug` and use _only_ the logging system for all output of the +application. + +But that's not what logs are targeted for -- and now I'm having to rethink +most of the stuff I already wrote. + +Use the standard output to inform the user what's going on, in a human +readable format; use the standard error output to inform the user when things +go wrong; but use the logs to capture something that you'll have to process +later, so you should probably use a format that it is easier to parse, even if +it is not so friendly. + +As an example, let's say you're connecting to a server. You could use the +standard output to say "Connecting to server", to give the user a feedback +about what's going on; at the same time, you could log "CONNECTION +[SERVER]", with the IP/Name of the server you're connecting. Surely, the +"CONNECTION" word is not that friendly to the user, but if you had to parse +the line, it would be really easy, wouldn't it? + +Another example: If your application is adding a record to the database, there +is nothing wrong logging "ADDING_RECORD: field=value; field=value; +field=value" 'cause, in case something goes wrong while saving the record, you +could have the values to try to figure out why it failed -- surely, logging +why it failed also helps, but you know what I mean. This is an example of +something that makes complete sense in logs, but not in user interfaces. diff --git a/things-i-learnt/src/programming/running/monitoring.md b/things-i-learnt/src/programming/running/monitoring.md index 1e601e9..3f1db71 100644 --- a/things-i-learnt/src/programming/running/monitoring.md +++ b/things-i-learnt/src/programming/running/monitoring.md @@ -1 +1,19 @@ # Learn To Monitor + +On a previous life, to understand how a system behaved, I added a ton of +metrics: how fast things were going in, how fast things were going out, how +many things were in the middle, how many the job processed... Not doing it so +makes me feel... naked. + +Monitoring your project performance give you a really good view of how a +system is behaving. Is the speed going down? Is the system taking longer to +process an input? Are no inputs being processed? + +If you have this kind of information, you can check what is going on in the +system and understand why. Is it normal? Did a change around the system (the +other system that produces the input, the system that consumes in the output) +affected the results? + +If you're not measuring, you'll have no idea. + +Also, "If you can not measure it, you can not improve it", as Lord Kevin said. diff --git a/things-i-learnt/src/programming/running/transparent.md b/things-i-learnt/src/programming/running/transparent.md index 1cda33e..6e05e19 100644 --- a/things-i-learnt/src/programming/running/transparent.md +++ b/things-i-learnt/src/programming/running/transparent.md @@ -1 +1,20 @@ # Be Transparent With The User + +Since we are talking about [logging](./log-events.md), +another thing you must do is to be transparent with the user in your user +interface. + +And by "be transparent", I meant that your website/mobile app needs to point +out to the user that the webserver is down instead of saying to the user to +check their internet connection; your application _is_ getting something from +the webserver, so you _can_ say "Oops, something wrong on our side". + +Another example: If you need to check a bunch of data before saying "It's +done", add a counter to show the user that the application is doing something. +[Joplin](https://joplinapp.org/), when syncing data with a webdav server, +needs to check a bunch of files; one version would simply sit still with a +spinner on "Syncing" and nothing more; when they added a counter, I could +easily see that there was something going on. + +Those small details, for as bad as they may make you look, will win points +with the user in the long run. diff --git a/things-i-learnt/src/programming/source-control.md b/things-i-learnt/src/programming/source-control.md index 5b34595..e3f9ae1 100644 --- a/things-i-learnt/src/programming/source-control.md +++ b/things-i-learnt/src/programming/source-control.md @@ -1 +1,3 @@ # Source Control + +Programming is coding and coding needs to be stored somewhere. diff --git a/things-i-learnt/src/programming/source-control/always-vcs.md b/things-i-learnt/src/programming/source-control/always-vcs.md index 499fd05..3ae8ef9 100644 --- a/things-i-learnt/src/programming/source-control/always-vcs.md +++ b/things-i-learnt/src/programming/source-control/always-vcs.md @@ -1 +1,23 @@ # Always Use A Version Control System + +"This is my stupid application that I just want to learn something" is not +even a good excuse to not use a version control system. + +A very long time ago, using a source control system (or Version Control +System) required installing a server, configuring it properly, installing the +client and _then_ you could keep track of the changes you were doing on your +code. + +Today there are lots of options that can work in a standalone fashion: Just +install the client and you're done (well, mostly done, you still need to +initialize the environment, but that is mostly straightforward these days). + +And, again, there is no good reason to not start a project, as simple as it +will be, without a version control. + +The VCS will allow you to explore new changes without breaking the main code. +It will allow you to save a half-way change to make a complete different +change. + +And, in the long, since you'll end up with working in team and will be +required to use a VCS, you'll be used to using one. diff --git a/things-i-learnt/src/programming/source-control/gerrit.md b/things-i-learnt/src/programming/source-control/gerrit.md index c64b355..4c2eec8 100644 --- a/things-i-learnt/src/programming/source-control/gerrit.md +++ b/things-i-learnt/src/programming/source-control/gerrit.md @@ -1 +1,26 @@ # Gerrit Is A Mistake + +I hate calling software "a mistake", but I can't find any other way to +describe Gerrit. You may see people using Gerrit 'cause Google uses it. The +thing is: Google misunderstood what Git actually is. + +When Linus Torvalds came with Git, he was trying to mimic another system, +BitKeeper. Along with some improvements over CVS and SubVersion, Git made +really easy to create and merge branches, something that was either +almost-not-supported or slow-as-heck, depending on which tool you look at. + +You need to take this into consideration: Git made branches easy. + +Then someone came with the idea of Gerrit: Instead of managing branches, it +actually manages _commits_. Instead of having a branch for each feature, you +should have _one single commit_ as feature. You can have branches on your +machine, but the server only deal with commits. + +So Gerrit took Git, a tool that improved the way we deal with branches, and +removed branches. This is akin to taking a text editor and taking away the +ability to _edit text_. Does that sound right to you? + +In my personal opinion, what they did was to take git apart and put an err in +the middle: gERRit. + +When I see someone using Gerrit, I know something is wrong there. diff --git a/things-i-learnt/src/programming/source-control/git-flow.md b/things-i-learnt/src/programming/source-control/git-flow.md index 08bd4b9..bafeda0 100644 --- a/things-i-learnt/src/programming/source-control/git-flow.md +++ b/things-i-learnt/src/programming/source-control/git-flow.md @@ -1 +1,19 @@ # Git-Flow Is The Way To Go + +If [Gerrit is such a mistake](./gerrit.md), what can you use +instead? Git Flow! + +Git Flow is a plugin for Git for managing branches. It is based on the concept +of "feature branches", in which each branch is a feature or bug you're working +on. Once you finish it, it will just close the branch. + +Although there is a lot to be said about Git and how you should use it, the +fact is that Git Flow manages a lot of complexity of having a stable branch, +an "unstable"/testing branch and all features around those. + +Not only that, but with the current source control sites like Github and +GitLab, the flow is quite similar -- although working with branches is changed +with forks. + +You can even install Git Flow and use it on your personal project -- which is +something I do with this blog/book! diff --git a/things-i-learnt/src/programming/source-control/one-change-commit.md b/things-i-learnt/src/programming/source-control/one-change-commit.md index 4548367..019a765 100644 --- a/things-i-learnt/src/programming/source-control/one-change-commit.md +++ b/things-i-learnt/src/programming/source-control/one-change-commit.md @@ -1 +1,29 @@ # One Commit Per Change + +When working with source control tools, keep one change per commit. Avoid +bundling more than one change in a single commit just to "save time". + +I've seen my fair share of commits with messages like "Fix issues #1, #2 +and #3". This is not something you should do. One commit for fixing issue #1, +another for #2 and yet another for #3. + +Just note that I said "one commit per change", not "one commit per file". +Sometimes, to make a single change, you may need to change more than one file +-- it may point that you have a coupling problem, but that's a different +issue. You could, for example, make one commit which adds a new field in model +without adding a change in the controller to load this field; after all, the +controller won't (or, at least, shouldn't) break due the added field, and the +model won't break (or, at least, shouldn't) break because the controller is +not touching the field[^1]. + +When making a commit, think this: "In case something goes wrong, can I undo +this commit without breaking other stuff?" Commit history is stacked, so +obviously you'd have to undo the commits on top of that one. And that's +alright. + +**BONUS TIP**! If you're using `git`, you can use `git add -p` in case you +"overchange". It will allow you to pick parts of a file, instead of adding all +the changes in the file before committing. + +[^1]: Ok, it _may_ have some issues if the field can't be null, but you get + what I meant, right? diff --git a/things-i-learnt/src/programming/testing.md b/things-i-learnt/src/programming/testing.md index 0d3d208..d2557aa 100644 --- a/things-i-learnt/src/programming/testing.md +++ b/things-i-learnt/src/programming/testing.md @@ -1 +1,4 @@ # Testing Software + +To make sure your software works, you have to think -- and write -- tests. But +are you actually testing something that is worth testing? diff --git a/things-i-learnt/src/programming/testing/integration-tests.md b/things-i-learnt/src/programming/testing/integration-tests.md index 0654ef7..cd5b854 100644 --- a/things-i-learnt/src/programming/testing/integration-tests.md +++ b/things-i-learnt/src/programming/testing/integration-tests.md @@ -1 +1,65 @@ # Unit Tests Are Good, Integration Tests Are Gooder + +The view of the whole is greater than the sum of its parts. And that includes +tests for the whole compared to tests of single things. + +First, I just don't want to into a discussion about what's the "unit in a unit +test"[^1], so let's take the point that a unit test is a test that tests a +class/function, not the whole system from end to end, which would require data +flowing through several classes/functions. + +There are several libraries/frameworks that actually split this in a way that +you can't test the whole. +[Spring](https://spring.io/)+[Mockito](https://site.mockito.org/) is one of +those combinations -- and one that I worked with. Due the bean container of +Java, the extensive use of Beans by Spring and the way Mockito interacts with +the container, it's pretty easy to write tests that involve only one class: +You can ask Mockito to mock every dependency injection (so it injects mocked +beans instead of the real ones) in one class and mock every injected class, +simply using annotations. + +And this is cool and all and makes tests simple and fast. But the fact that we +are making sure each class does what it should do, it doesn't give a proper +view of the whole; you can't see if that collection of perfectly tested +classes actually solve the problem the system is responsible for solving. + +Once, in C++, I wrote an alarm system +[daemon](https://en.wikipedia.org/wiki/Daemon_(computing)) for switches. There +were three different levels of things the alarm system should do, depending on +the incoming message from a service: It could only log the message of the +incoming error, it could log the error and send a SNMP message, or it could +log the error, send a SNMP message and turn a LED in the front panel on. +Because each piece had a well defined functionality, we broke the system in +three different parts: One for the log, one for the SNMP and one for the LED. +All tested, all pretty. But I still had a nagging feeling that something was +missing. That's when I wrote a test that would bring the daemon up, send some +alarms and see the results. + +And, although each module was well tested, we still got one things we were +doing it wrong. If we never wrote an integration test, we would never catch +those. + +Not only that, but because we wrote a test that interacted with the daemon, we +could get a better picture of its functionality and the test actually _made +sense_ -- as in, if you read the unit tests, they seemed disconnected from +what the daemon was expected to do, but the integration tests actually read +like "Here, let me show that we actually did what you asked". And yes, this +was akin to [Gherkin](../programming/gherkin.md) tests, although I didn't +know Gherkin at the time -- and, better yet, we had tests that proved that we +were following the [spec](../programming/spec-first.md). + +Personally, I think over time integration tests become more important than +unit tests. The reason is that I personally have the feeling[^2] that unit +tests check if the classes/functions have _adherence_ to the underlying +_design_ -- Does your view can actually work without the controller? Is the +controller using something from the model or using things that should be in +the view? -- but adherence to the design gets better over time -- developers +start using the layout from previous examples, so they capture the design by +osmosis, while the big picture starts to get more and more complex, with lots +of moving parts. + +[^1]: There is no "unit" in "unit tests". "Unit test" means the test _is_ a + unit, indivisible and dependent only on itself. + +[^2]: Again, it's pure feeling from my experience. I have no data to back that + affirmation up, so take it with a grain of salt. diff --git a/things-i-learnt/src/programming/testing/languages-tests.md b/things-i-learnt/src/programming/testing/languages-tests.md index 6485d5f..b31c12d 100644 --- a/things-i-learnt/src/programming/testing/languages-tests.md +++ b/things-i-learnt/src/programming/testing/languages-tests.md @@ -1 +1,15 @@ # Good Languages Come With Tests + +You can be sure that if a language brings a testing framework -- even minimal +-- in its standard library, the ecosystem around it will have better tests +than a language that doesn't carry a testing framework, no matter how good the +external testing frameworks for the language are. + +The reason is kinda obvious on this one: When the language itself brings a +testing framework, it reduces the friction for people to start writing tests, +and that includes the authors of the language itself and the community. + +Sure, better frameworks may come along, and languages that don't have a +testing framework in their standard library may have options with better +support and easier access but, again, when they are there from the start, the +start is better and the final result is better. diff --git a/things-i-learnt/src/programming/testing/tests-apis.md b/things-i-learnt/src/programming/testing/tests-apis.md index b058d92..9d33c96 100644 --- a/things-i-learnt/src/programming/testing/tests-apis.md +++ b/things-i-learnt/src/programming/testing/tests-apis.md @@ -1 +1,38 @@ # Tests Make Better APIs + +Testing things in isolation may give a better view of your APIs. + +After reading the [integration tests](./integration-tests.md) chapter, you may +end up with the impression that I don't like unit tests[^1]. + +Actually, I think they provide some good intrinsic values. + +For example, as mentioned before, they can provide a better look at the +adherence to the design. + +But, at the same time, they give a better view of your internal -- and even +external -- APIs. + +For example, you're writing the tests for the view layer -- 'cause, you know, +we write everything in layers; layers on top of layers[^2] -- and you're noticing +that you have to keep a lot of data (state) around to be able to make the +calls to the controller. Or that you have similar calls, but the parameters +are sometimes switched (say, one function gets a token and a user ID, and +another function gets a user ID and a token -- why?) That's a sign that you +may have to take a better look at the controller API. + +Not only that, but take, for example, the fact that you're working on a +library -- which will be called by someone else -- and you're writing tests +for the most external layer, the layer that will be exposed by that library. +And, again, you're noticing that you have to keep a lot of context around, +lots of variables, variables coming from different places and similar calls +using parameters in different ways. Your tests will look like a mess, don't +they? That's because the API _is_ a mess. + +Unit testing your layers makes you the _user_ of that layer API, and then you +can see how much one would suffer -- or, hopefully, enjoy -- using that. + +[^1]: Again, let's ignore for a second that there are no "unit" in "unit + tests"... +[^2]: And layers all the way down, [like + turtles](https://en.wikipedia.org/wiki/Turtles_all_the_way_down). diff --git a/things-i-learnt/src/programming/testing/tests-dead-code.md b/things-i-learnt/src/programming/testing/tests-dead-code.md index d56ea4f..f874741 100644 --- a/things-i-learnt/src/programming/testing/tests-dead-code.md +++ b/things-i-learnt/src/programming/testing/tests-dead-code.md @@ -1 +1,50 @@ # Testing Every Function Creates Dead Code + +If you write a test for every single function on your system, and your system +keeps changing, how will you know when a function is not necessary anymore? + +Writing a test for every single function on your system may come from the +"100% Coverage Syndrome", which afflicts some managers, thinking that the only +way to be completely sure your system is "bug free" is to write tests for +every single line of code, till you reach the magical "100% coverage" in all +the tests. + +I do believe you can reach 100% coverage, as long as you're willing to +_delete_ your code. + +(Cue the universal grasps here.) + +But how do you know which pieces of code can be deleted? + +When I mentioned [integration tests](./integration-tests), I mentioned how +much more sense it made to me reading them instead of the "unit" tests, +because they were describing exactly how the system would operate in normal +(and some abnormal) conditions. If you write tests that go through the system, +assuming it is a black box with an input point and an output, and you can get +tests for all the normal cases -- and some "abnormal", like when things go +wrong -- then you know that, if you run those tests and they mark some lines +as "not tested", it's because you don't need them. + +"But Julio, you're forgetting the error control!" I do agree, specially when +you're talking with project owners or some other expert, that people will +forget to tell you what to do in case of things going wrong -- say, the user +typing their name in the age field -- but _you_ can see those and _you_ know +that you need error control so _you_ can add the error control and describe +the situation where that error control would trigger. + +If, on the other hand, you write a test for every function, when you do a +short/simple check, you'll find that the function is still being used in the +system by the tests, not actually, "value to the user" code. Sure, you can +use your IDE to go back and forth between code and test and see if it points a +use beyond the test, but it won't do it for yourself. + +There is one other weird thing about using integration tests for error +controls: Sometimes, you can't reach the control statement. It's true! I did +wrote control checks for every function once but, when running in the +integration tests, there was no way to produce an input at the input layer of +the system that would reach the error control in that function 'cause the +other functions, which would run before the one I was trying to test, would +catch the error before it. If that's a design problem or not -- it probably +was -- it's a different discussion, but the fact is that that function didn't +need error control, something that I wouldn't see if I wrote test specifically +for it, but it was clear in an integration test run. diff --git a/things-i-learnt/src/programming/testing/tests-in-the-command-line.md b/things-i-learnt/src/programming/testing/tests-in-the-command-line.md index 0ed2ebd..a88e6d6 100644 --- a/things-i-learnt/src/programming/testing/tests-in-the-command-line.md +++ b/things-i-learnt/src/programming/testing/tests-in-the-command-line.md @@ -1 +1,27 @@ # Make Tests That You Know How To Run On The Command Line + +You know that "Play" with a little something on your IDE that runs only the +tests? Do you know what it does? + +A long time ago I read the story about a professor that taught his students to +code. He preferred to teach using an IDE, 'cause then "students have to just +press a button to run the tests". + +I get the idea, but I hate the execution. + +When we get into professional field, we start using things like [continuous +integration](https://en.wikipedia.org/wiki/Continuous_integration) which, +basically, is "run tests every time something changes" (it's a bit more than +that, but that's the basic idea). + +Now, let me ask you this: Do you think the students of the professor above +would know how to add the command to run the tests in a continuous +integration system? + +I know I'm being too picky (one could even call me "pricky" about this) but +the fact is that whatever we do today, at some point can be automated: our +tests can be run in an automated form, our deployment can be run in an +automated form, our validation can be run in an automated form and so on. If +you have no idea how those things "happen", you'll need the help of someone +else to actually build this kind of stuff, instead of having the knowledge +(well, half knowledge, the other half is the CI tool) with you all the time. diff --git a/things-i-learnt/src/teams/cargo-cult.md b/things-i-learnt/src/teams/cargo-cult.md index a681704..11bf57b 100644 --- a/things-i-learnt/src/teams/cargo-cult.md +++ b/things-i-learnt/src/teams/cargo-cult.md @@ -1 +1,31 @@ # Understand And Stay Away From Cargo Cult + +"[Cargo cult](https://en.wikipedia.org/wiki/Cargo_cult)" is a type of cult +which appeared in the Melanesia, in which the natives would build their copy +of an airplane (no motor, 'cause they didn't have the knowledge to build one +-- or even knew what went inside the airplane) in the hopes they would get the +same results as a real airplane. + +In IT, a "cargo cult" is the expectation that if you use the same tools as +some big players, you'd end up getting the same results. + +One example: Netflix runs a large fleet of microservices daily; they use +Spring Cloud; if we use Spring Cloud, we can also run a large fleet of +microservices. + +Although it may sound correct in a first glance, things are not like that. +There is much more to the Netflix fleet than just Spring Cloud. + +Sometimes, cargo cult can appear in a form of "fanaticism" about celebrities: +[Fowler](https://en.wikipedia.org/wiki/Martin_Fowler_(software_engineer)) said +such and such pattern works this way and that's exactly what we should do. +Just because Fowler is well know software engineer and architect and do have +some very clever ideas, picking them and running exactly the way he described +may do more harm than good -- basically, 'cause you'd end up applying a +design pattern without worrying about solving your problem in the first place. + +Another example: "ProductX is sponsored by BigCompany, so it's good". It may +be, but the fact that BigCompany is being ProductX doesn't immediately makes +ProductX good, or even if it fits your solution. And there is much more +[behind a product](./languages-are-more.md) than just its +development. diff --git a/things-i-learnt/src/teams/code-formatters.md b/things-i-learnt/src/teams/code-formatters.md index 32ff705..7cc272d 100644 --- a/things-i-learnt/src/teams/code-formatters.md +++ b/things-i-learnt/src/teams/code-formatters.md @@ -1 +1,39 @@ # Code Formatting Tools Are Ok, But No Silver Bullet + +One thing a team may decide to fix the continuous flux of code style comments +in a code review is to use a code formatting tool to auto-format the code. +That's ok, but they should never rely on it. + +Now yeah, that kinda solves the problem, but there is one small problem: +we, humans, are not as flexible to read code as computers are; what is +readable by a computer may not be readable by a human. Surely they try to +create some heuristics on what is good for human reading, but that doesn't mean +it gets right. + +Also, unless you start from scratch to use the auto-formatting tool or do a +change in all files in one single go, you should never assume it will do a +good job. + +I've seen tools like this implemented in a commit hook, in a way that the tool +would reformat the code just before adding it to the repository. The biggest +problem is that, in that team, we didn't run the auto-formatting tool in the +whole project before hand, and we also added a coverage tool (that checked the +coverage on the changed parts of the file) without every running the coverage +tool on everything. The result is that, suddenly, a lot of commits got refused +because the auto-formatting tool was changing lines that the developer didn't +change (it changed old code) and suddenly the coverage tool noted the missed +tests and decided it was no good. + +So good, punctual changes were suddenly being blocked 'cause instead of doing +the whole thing in a single shot, people decided it was a good idea to let the +code evolve till everything fixed itself. + +On top of that, some people who were neither in the mood to actually add the +tests or worried about style found a way to do the commits _without running +the hook_, so they basically skipped the whole "let's improve our code" and +let fuck all. + +So, it's ok if you run the auto-formatting tool for yourself, but you need to +have the maturity and responsibility to watch yourself and be willing to fix +and take responsibility for other people's code when the formatter changes +their code. diff --git a/things-i-learnt/src/teams/code-reviews-style.md b/things-i-learnt/src/teams/code-reviews-style.md index 95ae562..1510fce 100644 --- a/things-i-learnt/src/teams/code-reviews-style.md +++ b/things-i-learnt/src/teams/code-reviews-style.md @@ -1 +1,22 @@ # Code Reviews Are Not For Style + +When doing code reviews, do not focus on style; focus on design things that +look a bit weird. + +Code reviews are designed to spread knowledge around the team, with focus on +construction and problem description. Does the sequence of code gives you an +understanding on what it is trying to do? Does it contains something you know +it will make things harder to read in the future? Does it miss the point? + +That's the things you should focus. + +If the author of the code missed a space here, left a blank line there... +that's of no consequence in a code review. This is not the thing to discuss in +the code review. + +And people _hate_ people who go through code reviews just to point +missing/extra spaces and lines. + +On the other hand, if you find something weird in the code which is something +you want the author to recheck, _then_ you're free to comment that "it would +be good" if they fix the style. But that's it. diff --git a/things-i-learnt/src/teams/code-style.md b/things-i-learnt/src/teams/code-style.md index e09ac4a..da42136 100644 --- a/things-i-learnt/src/teams/code-style.md +++ b/things-i-learnt/src/teams/code-style.md @@ -1 +1,24 @@ # Code Style: Follow It + +If your project have a defined code style, you must follow it. Sometimes it +may not be clear ("this struct/class should be singular or plural"?), but do +your best to follow it. + + + +If your project doesn't have a style, maybe it's time to pick one. There are +well established styles for almost every language today, so you can start with +that. You can even make your changes, but you need to realize that since it's +been established for a while, a lot of other people are using that style and, +thus, if you keep as is, your code will mesh better with the rest of the +ecosystem. + +And remember that even your stupid code is [part of the ecosystem of the +language](./languages-are-more.md) and the better you +interact with the ecosystem, the better citizen in the ecosystem you are. + +**TIP**: If you don't have a code style yet, and you're using a language +that's derived from C or C++, use [K&R +Style](https://en.wikipedia.org/wiki/Indentation_style#K&R_style); if you're +working with Python, there is only one style: +[PEP8](https://www.python.org/dev/peps/pep-0008/). diff --git a/things-i-learnt/src/teams/google-code-style.md b/things-i-learnt/src/teams/google-code-style.md index 362b676..3f6eb72 100644 --- a/things-i-learnt/src/teams/google-code-style.md +++ b/things-i-learnt/src/teams/google-code-style.md @@ -1 +1,9 @@ # ... Unless That Code Style Is The Google Code Style + +Every freaking time Google comes with their own coding style, it's a garbage +fire. The community came with a better style way before and Google seem to +come with a style with high contrasting parts just to call it theirs. + +The only reason to use Google Code Style is in case someone less smart than +you decided it would be a good idea to use it. Then, I feel sorry for you, but +you'll have to follow Google Code Style. diff --git a/things-i-learnt/src/teams/hero-projects.md b/things-i-learnt/src/teams/hero-projects.md index ec836ba..7c86a85 100644 --- a/things-i-learnt/src/teams/hero-projects.md +++ b/things-i-learnt/src/teams/hero-projects.md @@ -1 +1,21 @@ # Hero Projects: You'll Have To Do It Yourself + +An "hero project" is a project/spec change that you personally think will +solve a group of problems in your project. It could be a different +architecture, a new framework or even a new language. + +Hero projects happen mostly when a single developer wants to prove something +without the support of the company or even the time they are in. + +On those projects, developers will spend their free time to write a +proof-of-concept, just to prove a point. + +And, sometimes, it just proves that they are were wrong. + +(Although that last point sounds a bit sad, if you have to do an hero project, +you'll still learn something new and, maybe, even add a new bullet point to +your CV.) + +Just to be clear: Sometimes an hero project will fail [because the answer is +obvious](./right-tool-obvious.md). Don't let that make you +feel down. diff --git a/things-i-learnt/src/teams/index.md b/things-i-learnt/src/teams/index.md index cd5f674..cc4e3cb 100644 --- a/things-i-learnt/src/teams/index.md +++ b/things-i-learnt/src/teams/index.md @@ -1 +1,4 @@ # Community/Teams + +Programming is barely a solo endeavour. You'll have to deal with more people +when working on your projects. diff --git a/things-i-learnt/src/teams/languages-are-more.md b/things-i-learnt/src/teams/languages-are-more.md index 3c2e4a3..c06ad99 100644 --- a/things-i-learnt/src/teams/languages-are-more.md +++ b/things-i-learnt/src/teams/languages-are-more.md @@ -1 +1,47 @@ # A Language Is Much More Than A Language + +Picking a programming language is much more than just picking the words that +will generate a code. They come with a community, a leadership, an ecosystem +and a thread the binds them all together. + +Programming languages, in essence, are simply a bunch of keywords that make +things "go". But besides those keywords, they also bring their community, the +way the leaders deal with the community, the tools created by the leaders or +community to deal with the minutiae of creating a system, the way those tools +interact with each other, and a lot more. + +While a language may have a simple syntax, it may be that the ones controlling +the language actually don't give two shits -- if you pardon my French -- to +the community. They focus on solving _their_ problems, not the community +problems[^1]. + +Or maybe the community has duplicate tools -- which is not a problem -- but +that developers of each tool don't talk to each other. Or worse: They simply +refuse to look what other tools are doing, which could be used to improve +their own[^2]. + +And maybe that third language is not as simple as others, but the leadership +is always discussing things with the community, being transparent on their +decision, allowing the community to discuss the future of the language and +even different groups building tools decided to merge efforts to give the +community better tools. + +That's why you can't "pick" a language by its syntax alone. That's only the +surface of what the whole of a language encapsulates and if you ignore the +other elements in it, you may find yourself with a cute language in a +community that is always fighting and never going forward. + +And picking a language for something _above_ the syntax is even worse. + +[^1]: Yes, this is common, even in larger communities. And yes, I've seen the + leadership ignoring requests from the community and, sometimes, just + ignoring all the hard work the community did to supply the missing bits + because they didn't like it. +[^2]: Again, I've seen this before: There was a language that didn't come with + a build tool bundled. The community created a tool, which was widely + adopted. Later, a new build tool appeared and, in one of the notes, the + author of the new tool mentioned a feature. The community came and asked + "The previous build tool did something like that, what's the difference + between that and your tool?" And the answer was "I never used the first + tool." So, basically, the community ignored whatever the community was + using. diff --git a/things-i-learnt/src/teams/right-tool-agenda.md b/things-i-learnt/src/teams/right-tool-agenda.md index 59ca552..c99f5c2 100644 --- a/things-i-learnt/src/teams/right-tool-agenda.md +++ b/things-i-learnt/src/teams/right-tool-agenda.md @@ -1 +1,14 @@ # "Right Tool For The Job" Is Just To Push An Agenda + +A lot of times I heard "We should use the right tool for the job!" Most of +those times it was just a way to push an agenda. + +When someone claims we should use the "right tool", the sentence mean there is +a right tool and a wrong tool to do something -- e.g., using a certain +language/framework instead of the current language/framework. + +But sadly, none of those times it was really the "right tool". Most of the +time, the person saying we should use the "right tool" was trying to push +their own favourite language/framework, either because they disliked the +current language/framework or because they don't want to push the "hero +project". diff --git a/things-i-learnt/src/teams/right-tool-obvious.md b/things-i-learnt/src/teams/right-tool-obvious.md index 6244cf7..5669605 100644 --- a/things-i-learnt/src/teams/right-tool-obvious.md +++ b/things-i-learnt/src/teams/right-tool-obvious.md @@ -1 +1,19 @@ # The Right Tool Is More Obvious Than You Think + +Maybe you're in a project that needs to process some text. Maybe you're +tempted to say "Let's use Perl" 'cause you know that Perl is very strong in +processing text. + +But that may still be not the right tool. + +Although Perl is an amazing tool to process files, providing every single +switch and option you'll ever need, you're missing something: You're working +on a C shop. Everybody knows C, not Perl. + +Sure, if it is a small, "on the corner" kind of project, it's fine to be in +Perl; if it is important for the company, it's better that if it is a C +project. + +One of the reason your hero project may fail is because of this: You may even +prove that what you thought it was a better solution is actually a better +solution, but it can't be applied 'cause nobody else can maintain it. diff --git a/things-i-learnt/src/teams/team-discussion.md b/things-i-learnt/src/teams/team-discussion.md index 913077c..f295aad 100644 --- a/things-i-learnt/src/teams/team-discussion.md +++ b/things-i-learnt/src/teams/team-discussion.md @@ -1 +1,32 @@ # Global Changes Must Be Discussed With The Whole Team First + +So you got tired of bad tests and decided it is a good idea to add some [fuzz +testing](https://en.wikipedia.org/wiki/Fuzzing) tool. Before you do add it in +the main branch, you _have_ to discuss it with your team. + +It's mind-bogging that some people think something it's so good that they +don't need to discuss with the whole team about it; they simply do. They don't +seem to care that people have their workflows and changing something would +break them. But hey, I've seen it so many times it is not even fun. + +And let me clear here: You need to discuss it with the _whole_ team, not just +some of it (excluding people on vacations, 'cause you don't want to call them +just to tell them something will change). Worse: Don't discuss only with those +that will agree with you; you may not have seen all the problems those changes +will inflict on the other devs workflows but, by bringing that with those that +may not agree with you, you may gain some more insights on what could go +wrong. + +Also, focus on what would be the gains and the loses. "We'll get better tests, +but you'll have to take a bit more care on the way you write tests" is a good +explanation, specially if you show the changes people will have to do in +future tests. Also also, notice that I said _future_ tests: if you want to +implement something new, you _must_ be sure it won't require everyone getting +out of their way to make your idea work -- don't make people rewrite tests +'cause they will break; don't make the other devs reformat their code 'cause +you decided, alone, to add a linter to your CI with your own rules; don't make +people worry about unwritten tests 'cause you decided it would be a good idea +to add a code formatting tool and that would make your coverage tool think +they are changing some unrelated piece of code that wasn't untested before. + +Don't be a jerk thinking you know more than your whole team.