Browse Source

Content!

master
Julio Biason 4 years ago
parent
commit
73c07a7ea9
  1. 18
      things-i-learnt/src/personal/bad-code-defense.md
  2. 22
      things-i-learnt/src/personal/blogging.md
  3. 16
      things-i-learnt/src/personal/coc.md
  4. 20
      things-i-learnt/src/personal/fixable.md
  5. 19
      things-i-learnt/src/personal/hero-syndrome.md
  6. 24
      things-i-learnt/src/personal/learn-about-yourself.md
  7. 44
      things-i-learnt/src/personal/microaggressions.md
  8. 22
      things-i-learnt/src/personal/not-done.md
  9. 35
      things-i-learnt/src/personal/own-your-shit.md
  10. 11
      things-i-learnt/src/personal/people-care.md
  11. 23
      things-i-learnt/src/personal/post-solution.md
  12. 19
      things-i-learnt/src/personal/quit.md
  13. 37
      things-i-learnt/src/personal/responsible-code.md
  14. 16
      things-i-learnt/src/personal/say-no.md
  15. 25
      things-i-learnt/src/personal/small-world.md
  16. 32
      things-i-learnt/src/personal/specialists.md
  17. 13
      things-i-learnt/src/personal/stupid-bugs-list.md
  18. 11
      things-i-learnt/src/personal/things-i-dont-know.md
  19. 14
      things-i-learnt/src/personal/time-to-stop.md
  20. 26
      things-i-learnt/src/personal/time.md
  21. 23
      things-i-learnt/src/personal/toxic-people.md
  22. 20
      things-i-learnt/src/personal/watch-reactions.md
  23. 45
      things-i-learnt/src/programming/before/cognitive-cost.md
  24. 24
      things-i-learnt/src/programming/before/data-flow.md
  25. 29
      things-i-learnt/src/programming/before/debuggers.md
  26. 57
      things-i-learnt/src/programming/before/functional-programming.md
  27. 98
      things-i-learnt/src/programming/before/magical-number-seven.md
  28. 29
      things-i-learnt/src/programming/before/understand-shortcuts.md
  29. 24
      things-i-learnt/src/programming/before/users.md
  30. 2
      things-i-learnt/src/programming/coding.md
  31. 21
      things-i-learnt/src/programming/coding/boolean-parameters.md
  32. 40
      things-i-learnt/src/programming/coding/crash-it.md
  33. 30
      things-i-learnt/src/programming/coding/data-types.md
  34. 18
      things-i-learnt/src/programming/coding/future-trashing.md
  35. 26
      things-i-learnt/src/programming/coding/handle-it.md
  36. 23
      things-i-learnt/src/programming/coding/interface-changes.md
  37. 27
      things-i-learnt/src/programming/coding/optimization.md
  38. 32
      things-i-learnt/src/programming/coding/outside-project.md
  39. 30
      things-i-learnt/src/programming/coding/permanent-solution.md
  40. 20
      things-i-learnt/src/programming/coding/resist-easy.md
  41. 31
      things-i-learnt/src/programming/coding/run-locally.md
  42. 23
      things-i-learnt/src/programming/coding/start-stupid.md
  43. 31
      things-i-learnt/src/programming/coding/throw-away.md
  44. 18
      things-i-learnt/src/programming/coding/units.md
  45. 61
      things-i-learnt/src/programming/coding/use-structures.md
  46. 30
      things-i-learnt/src/programming/coding/use-timezones.md
  47. 44
      things-i-learnt/src/programming/coding/use-utf8.md
  48. 2
      things-i-learnt/src/programming/documentation.md
  49. 22
      things-i-learnt/src/programming/documentation/document-and.md
  50. 28
      things-i-learnt/src/programming/documentation/document-is-contract.md
  51. 36
      things-i-learnt/src/programming/documentation/document-it.md
  52. 28
      things-i-learnt/src/programming/documentation/languages-docs.md
  53. 3
      things-i-learnt/src/programming/organization.md
  54. 37
      things-i-learnt/src/programming/organization/libraries.md
  55. 13
      things-i-learnt/src/programming/organization/paper-notes.md
  56. 73
      things-i-learnt/src/programming/organization/project-organization.md
  57. 3
      things-i-learnt/src/programming/running.md
  58. 22
      things-i-learnt/src/programming/running/add-then-remove.md
  59. 19
      things-i-learnt/src/programming/running/app-composition-stupid.md
  60. 33
      things-i-learnt/src/programming/running/application-composition.md
  61. 21
      things-i-learnt/src/programming/running/command-line-options.md
  62. 44
      things-i-learnt/src/programming/running/config-file.md
  63. 35
      things-i-learnt/src/programming/running/log-events.md
  64. 18
      things-i-learnt/src/programming/running/monitoring.md
  65. 19
      things-i-learnt/src/programming/running/transparent.md
  66. 2
      things-i-learnt/src/programming/source-control.md
  67. 22
      things-i-learnt/src/programming/source-control/always-vcs.md
  68. 25
      things-i-learnt/src/programming/source-control/gerrit.md
  69. 18
      things-i-learnt/src/programming/source-control/git-flow.md
  70. 28
      things-i-learnt/src/programming/source-control/one-change-commit.md
  71. 3
      things-i-learnt/src/programming/testing.md
  72. 64
      things-i-learnt/src/programming/testing/integration-tests.md
  73. 14
      things-i-learnt/src/programming/testing/languages-tests.md
  74. 37
      things-i-learnt/src/programming/testing/tests-apis.md
  75. 49
      things-i-learnt/src/programming/testing/tests-dead-code.md
  76. 26
      things-i-learnt/src/programming/testing/tests-in-the-command-line.md
  77. 30
      things-i-learnt/src/teams/cargo-cult.md
  78. 38
      things-i-learnt/src/teams/code-formatters.md
  79. 21
      things-i-learnt/src/teams/code-reviews-style.md
  80. 23
      things-i-learnt/src/teams/code-style.md
  81. 8
      things-i-learnt/src/teams/google-code-style.md
  82. 20
      things-i-learnt/src/teams/hero-projects.md
  83. 3
      things-i-learnt/src/teams/index.md
  84. 46
      things-i-learnt/src/teams/languages-are-more.md
  85. 13
      things-i-learnt/src/teams/right-tool-agenda.md
  86. 18
      things-i-learnt/src/teams/right-tool-obvious.md
  87. 31
      things-i-learnt/src/teams/team-discussion.md

18
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.

22
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.

16
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.

20
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?

19
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.

24
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.

44
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.

22
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.

35
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.

11
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.

23
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.

19
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_).

37
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.

16
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.

25
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.

32
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.

13
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.

11
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.

14
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.

26
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.

23
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).

20
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.

45
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.

24
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.

29
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.

57
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.

98
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.

29
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.

24
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.

2
things-i-learnt/src/programming/coding.md

@ -1 +1,3 @@
# Writing code
Let's put those things to work!

21
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.

40
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.

30
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.

18
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".

26
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.

23
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.)

27
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.

32
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.

30
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.

20
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.

31
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.

23
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.

31
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).

18
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.

61
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?

30
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".

44
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.

2
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?

22
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).

28
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.
<!-- more -->
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.

36
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... :(

28
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.

3
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?

37
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.

13
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!

73
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.

3
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?

22
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.

19
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.

33
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.

21
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.

44
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).

35
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.

18
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.

19
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.

2
things-i-learnt/src/programming/source-control.md

@ -1 +1,3 @@
# Source Control
Programming is coding and coding needs to be stored somewhere.

22
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.

25
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.

18
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!

28
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?

3
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?

64
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.

14
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.

37
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).

49
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.

26
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.

30
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.

38
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.

21
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.

23
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.
<!-- more -->
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/).

8
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.

20
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.

3
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.

46
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.

13
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".

18
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.

31
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.

Loading…
Cancel
Save