@ -36,4 +36,16 @@ 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
Then, when they crash, you can think of a way to deal with it, instead of
silently capturing it and doing nothing.
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.
{{ chapters(prev_chapter_link="/books/things-i-learnt/interface-changes", prev_chapter_title="Beware of Interface Changes", next_chapter_link="/books/things-i-learnt/handle-it", next_chapter_title="If You Know How To Handle It, Handle It") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/interface-changes", prev_chapter_title="Beware of Interface Changes", next_chapter_link="/books/things-i-learnt/handle-it", next_chapter_title="If You Know How To Handle It, Handle It") }}
@ -22,19 +22,20 @@ typing. The same still applies.
One classic example of misusing types is adding booleans. Booleans are either
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
`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
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`. And
with the value `1` and `FALSE` with another integer with the value `0`. Newer
so, the build on top of what older developers knew, other languages use the
languages were build on top of what older developers knew, and so, a bunch of
same concepts. And so, you have a list of booleans and want to know how many
those languages also assumed using an integer under booleans was a good idea.
true values are in the list, you can simply add them all.
And even today, with modern languages, people rely on those old methods.
Let me point that again: You're adding booleans and expecting a number.
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
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 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
the resulting list size. You're jumping the underlying type to get a bit of
performance out.
performance out.
Fortunately, new languages are based on ML, which wouldn't allow this kind of
Fortunately, some new languages are using booleans as a complete different
stuff.
type and wouldn't allow this kind of stuff.
{{ chapters(prev_chapter_link="/books/things-i-learnt/handle-it", prev_chapter_title="If You Know How To Handle It, Handle It", next_chapter_link="/books/things-i-learnt/use-structures", next_chapter_title="If Your Data Has a Schema, Use a Structure") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/handle-it", prev_chapter_title="If You Know How To Handle It, Handle It", next_chapter_link="/books/things-i-learnt/use-structures", next_chapter_title="If Your Data Has a Schema, Use a Structure") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/document-is-contract", prev_chapter_title="The Function Documentation Is Its Contract", next_chapter_link="/books/things-i-learnt/languages-docs", next_chapter_title="Good Languages Come With Integrated Documentation") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/document-is-contract", prev_chapter_title="The Function Documentation Is Its Contract", next_chapter_link="/books/things-i-learnt/languages-docs", next_chapter_title="Good Languages Come With Integrated Documentation") }}
@ -23,6 +23,16 @@ 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,
congratulations, you won't have a problem in the future; if you can't... well,
I have some bad news for you...
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?
That's the things you need to document.
[^1]: Please, don't make me revise this in 2027... :(
[^1]: Please, don't make me revise this in 2027... :(
{{ chapters(prev_chapter_link="/books/things-i-learnt/languages-tests", prev_chapter_title="Good Languages Come With Tests", next_chapter_link="/books/things-i-learnt/document-is-contract", next_chapter_title="The Function Documentation Is Its Contract") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/languages-tests", prev_chapter_title="Good Languages Come With Tests", next_chapter_link="/books/things-i-learnt/document-is-contract", next_chapter_title="The Function Documentation Is Its Contract") }}
@ -12,9 +12,9 @@ will solve all the problems, including the ones that may appear in the future.
<!-- more -->
<!-- more -->
Trying to solve the problems that will appear in the future comes with a hefty
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 _never_
tax: future problems future will never come -- and, believe me, they will
come -- and you'll end up either having to maintain a huge behemoth of code
_never_ come -- and you'll end up either having to maintain a huge behemoth of
that will never be fully used or you'll end up rewriting the whole thing
code that will never be fully used or you'll end up rewriting the whole thing
'cause there is a shitton of unused stuff.
'cause there is a shitton of unused stuff.
Solve the problem you have right now. Then solve the next one. And the next
Solve the problem you have right now. Then solve the next one. And the next
@ -23,4 +23,7 @@ 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
_abstraction_ you're looking for and _then_ you'll be able to solve it in a
simple way.
simple way.
As Steve Jobs once said "You can't connect the dots looking forward, only
backwards".
{{ chapters(prev_chapter_link="/books/things-i-learnt/throw-away", prev_chapter_title="Be Ready To Throw Your Code Away", next_chapter_link="/books/things-i-learnt/boolean-parameters", next_chapter_title="Don't Use Booleans As Parameters") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/throw-away", prev_chapter_title="Be Ready To Throw Your Code Away", next_chapter_link="/books/things-i-learnt/boolean-parameters", next_chapter_title="Don't Use Booleans As Parameters") }}
@ -16,17 +16,17 @@ Programming languages, in essence, are simply a bunch of keywords that make
things "go". But besides those keywords, they also bring their community, the
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
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
community to deal with the minutiae of creating a system, the way those tools
interact with each other.
interact with each other, and a lot more.
While a language may have a simple syntax, it may be that the ones controlling
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 language actually don't give two shits -- if you pardon my French -- to
the community. They focus on solving _their_ problems, not the community
the community. They focus on solving _their_ problems, not the community
problems.
problems[^1].
Or maybe the community has duplicate tools -- which is not a problem -- but
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
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
refuse to look what other tools are doing, which could be used to improve
their own.
their own[^2].
And maybe that third language is not as simple as others, but the leadership
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
is always discussing things with the community, being transparent on their
@ -39,4 +39,19 @@ 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
other elements in it, you may find yourself with a cute language in a
community that is always fighting and never going forward.
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.
{{ chapters(prev_chapter_link="/books/things-i-learnt/log-events", prev_chapter_title="Logs Are For Events, Not User Interface", next_chapter_link="/books/things-i-learnt/outside-project", next_chapter_title="Don't Mess With Things Outside Your Project") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/log-events", prev_chapter_title="Logs Are For Events, Not User Interface", next_chapter_link="/books/things-i-learnt/outside-project", next_chapter_title="Don't Mess With Things Outside Your Project") }}
@ -12,20 +12,22 @@ necessarily an human readable format.
<!-- more -->
<!-- more -->
For a long time, I used logs to show to the user what was happening. Nothing
For a long time, I used logs to show to the user what was happening. To me, it
could be the idea of displaying errors, general information and, if the user
was logical to use something where I could mark errors as errors, general
requested, the internals of whatever is going on in one simple way. I mean,
information as information and, if the user requested more information, print
yeah, let me add the errors, the info messages and the debug messages and let
more information on what was going on. So I just added logging, defined normal
the user decide what they want to see.
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
But that's not what logs are targeted for -- and now I'm having to rethink
most of the stuff I already wrote.
most of the stuff I already wrote.
Use the standard output to inform the user what's going on, in a human
Use the standard output to inform the user what's going on, in a human
readable format; use the standard err to inform the user when things go wrong;
readable format; use the standard error output to inform the user when things
but use the logs to capture something that you'll have to process later, so
go wrong; but use the logs to capture something that you'll have to process
you should probably use a format that it is easier to parse, even if it is not
later, so you should probably use a format that it is easier to parse, even if
so friendly.
it is not so friendly.
As an example, let's say you're connecting to a server. You could use the
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
standard output to say "Connecting to server", to give the user a feedback
@ -41,4 +43,4 @@ 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
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.
something that makes complete sense in logs, but not in user interfaces.
{{ chapters(prev_chapter_link="/books/things-i-learnt/start-stupid", prev_chapter_title="Start Stupid", next_chapter_link="/books/things-i-learnt/languages-are-more", next_chapter_title="A Language Is Much More Than A Language") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/use-utf8", prev_chapter_title="Always Use UTF-8 For Your Strings", next_chapter_link="/books/things-i-learnt/languages-are-more", next_chapter_title="A Language Is Much More Than A Language") }}
tags = ["en-au", "books", "things i learnt", "frameworks"]
tags = ["en-au", "books", "things i learnt", "frameworks"]
+++
+++
Simple rule: Is the code yours or from your team? Good, go break it. Does it
Simple rule: Is the code yours or from your team? Good, you can make any
come from outside? DON'T. TOUCH. IT.
changes you want. Does it come from outside? DON'T. TOUCH. IT.
<!-- more -->
<!-- more -->
@ -32,4 +32,12 @@ thing using extensions or actually coding around the problem, even duplicating
the framework functions, would probably take longer and make you write more
the framework functions, would probably take longer and make you write more
code, but in the long run, it's worth the time.
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.
{{ chapters(prev_chapter_link="/books/things-i-learnt/use-structures", prev_chapter_title="If Your Data Has a Schema, Use a Structure", next_chapter_link="/books/things-i-learnt/resist-easy", next_chapter_title="Resist The Temptation Of Easy") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/use-structures", prev_chapter_title="If Your Data Has a Schema, Use a Structure", next_chapter_link="/books/things-i-learnt/resist-easy", next_chapter_title="Resist The Temptation Of Easy") }}
@ -11,14 +11,14 @@ easily build your project, but do you understand what's going on?
<!-- more -->
<!-- more -->
I'm not denying the fact that IDEs make things easier. The fact is, you should
I'm not denying the fact that IDEs make things easier. I'm trying to say that
not rely heavily on their features.
you should not rely heavily on their features.
I mentioned before that you should at least know how to [run tests on the
I mentioned before that you should at least know how to [run tests on the
command line](/books/things-i-learnt/tests-in-the-command-line) and the same
command line](/books/things-i-learnt/tests-in-the-command-line) and the same
applies to everything in IDEs: how to build, how to run, how to run tests and,
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
let's be honest here, how to find proper names for your variables and
functions. 'Cause, sure, it's nice that the IDE can complete all the names of
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
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
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
a good name for your function so you _won't_ need to use autocomplete to
@ -28,4 +28,4 @@ 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
even how to name your variables. But using the autocomplete is not always a
good solution. Finding better names is.
good solution. Finding better names is.
{{ chapters(prev_chapter_link="/books/things-i-learnt/outside-project", prev_chapter_title="Don't Mess With Things Outside Your Project", next_chapter_link="/books/things-i-learnt/use-timezones", next_chapter_title="Always Use Timezones With Your Dates") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/outside-project", prev_chapter_title="Don't Mess With Things Outside Your Project", next_chapter_link="/books/things-i-learnt/start-stupid", next_chapter_title="Start Stupid") }}
But starting it in the stupid way, in which you have to think your project
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
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
you some insights on how things work, how the pieces mesh together and how to
cogs turn around.
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
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
favourite IDE and do things in the easy way. But you can also have that side
@ -30,4 +31,4 @@ what your IDE is doing.
And when you grasp that, you'll be able to use _any_ IDE.
And when you grasp that, you'll be able to use _any_ IDE.
{{ chapters(prev_chapter_link="/books/things-i-learnt/use-utf8", prev_chapter_title="Always Use UTF-8 For Your Strings", next_chapter_link="/books/things-i-learnt/log-events", next_chapter_title="Logs Are For Events, Not User Interface") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/resist-easy", prev_chapter_title="Resist The Temptation Of Easy", next_chapter_link="/books/things-i-learnt/use-timezones", next_chapter_title="Always Use Timezones With Your Dates") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/integration-tests", prev_chapter_title="Unit Tests Are Good, Integration Tests Are Gooder", next_chapter_link="/books/things-i-learnt/tests-in-the-command-line", next_chapter_title="Make Tests That You Know How To Run on the Command line") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/integration-tests", prev_chapter_title="Unit Tests Are Good, Integration Tests Are Gooder", next_chapter_link="/books/things-i-learnt/tests-in-the-command-line", next_chapter_title="Make Tests That You Know How To Run on the Command line") }}
No matter if the date you're receiving is in your local timezone and you'll
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
display it in your timezone, sooner or later, the fact that you ignored there
was a timezone behind that date will hurt you.
was a timezone behind that date will hurt you.
<!-- more -->
<!-- more -->
(Note: Most of this post when I say "date" you can think of "date and time",
(Note: Most of this post when I say "date" you can think of "date and time",
although the date should also be timezone aware.)
although the date should also be "timezone aware".)
At some point of my professional life, ignoring timezones was easy: You just
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
pick the date, throw in the database, then read it back and everybody was
@ -32,9 +32,10 @@ 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
Modules/classes that don't support timezones for dates/times should, as soon
as possible, removed from the system.
as possible, removed from the system.
Developers a bit more seasoned -- and by "seasoned" I meant "Had to deal with
Any developers a bit more seasoned -- and by "seasoned" I meant "Had to deal
times before" -- will probably claim "Hey, this is _obvious_!" And I'd have to
with times before" -- will probably claim "Hey, this is _obvious_!" And I'd
agree. But it's annoying how many times I got bitten by some stupid bug 'cause
have to agree. But it's annoying how many times I got bitten by some stupid
we decided that "well, everything is in the same timezone, so it's all good".
bug 'cause we decided that "well, everything is in the same timezone, so it's
all good".
{{ chapters(prev_chapter_link="/books/things-i-learnt/resist-easy", prev_chapter_title="Resist The Temptation Of Easy", next_chapter_link="/books/things-i-learnt/use-utf8", next_chapter_title="Always Use UTF-8 For Your Strings") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/start-stupid", prev_chapter_title="Start Stupid", next_chapter_link="/books/things-i-learnt/use-utf8", next_chapter_title="Always Use UTF-8 For Your Strings") }}
@ -12,11 +12,11 @@ with no "weird" or "funny" characters.
<!-- more -->
<!-- more -->
I was born in a time when the only encoding we had was ASCII. You could encode
I became a developer in a time when the only encoding we had was ASCII. You
all strings in sequences of bytes, 'cause all characters you could use where
could encode all strings in sequences of bytes, 'cause all characters you
encoded from 1 to 255 (well, from 32 [space] to 93 [close brackets] and you
could use where encoded from 1 to 255 (well, from 32 [space] to 93 [close
still have a few latin-accented characters in some higher positions, although
brackets] and you still have a few latin-accented characters in some higher
not all accents where there).
positions, although not all accents where there).
Today, accepting characters beyond that is not the exception, but the norm. To
Today, accepting characters beyond that is not the exception, but the norm. To
cope with all that, we have things like
cope with all that, we have things like
@ -26,18 +26,18 @@ memory space (UTF-16 is also a good option here, but that would depend on your
language).
language).
So, as much as you to make your system simple, you will have to keep the
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. Surely, you may not
internal representation of your strings in UTF-8/UTF-16. You may not receive
receive the data as UTF-8/UTF-16, but you'll have to encode it and keep
the data as UTF-8/UTF-16, but you'll have to encode it and keep transmitting
transmitting it around as UTF-8/UTF-16 till you have to display it, at which
it around as UTF-8/UTF-16 till you have to display it, at which point you'll
point you'll convert from UTF-8/UTF-16 to whatever your display supports
convert from UTF-8/UTF-16 to whatever your display supports (maybe it even
(maybe it even supports displaying in UTF-8/UTF-16, so you're good already).
supports displaying in UTF-8/UTF-16, so you're good already).
At this point, I believe most languages do support UTF-8, which is great. You
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
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
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
-- the hard part is figuring out the input _encoding_, though. Also, most
developers tend to ignore this and only accept ASCII characters, or ignore
developers tend to ignore this and assume the input is in ASCII, or ignore the
UTF-8/whatever-encoding and get a bunch of weird characters on their printing,
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
'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,
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.
encode it in UTF-8 and _then_ convert in the output.
@ -52,4 +52,4 @@ single character. Walking through the whole string would require traversing
the string character by character, instead of simply jumping straight to the
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.
proper position. But that's a price worth paying, in the long run.
{{ chapters(prev_chapter_link="/books/things-i-learnt/use-timezones", prev_chapter_title="Always Use Timezones With Your Dates", next_chapter_link="/books/things-i-learnt/start-stupid", next_chapter_title="Start Stupid") }}
{{ chapters(prev_chapter_link="/books/things-i-learnt/use-timezones", prev_chapter_title="Always Use Timezones With Your Dates", next_chapter_link="/books/things-i-learnt/log-events", next_chapter_title="Logs Are For Events, Not User Interface") }}