
Things I learnt the hard way in thirty years of software development - pbowyer
https://blog.juliobiason.net/thoughts/things-i-learnt-the-hard-way/
======
koonsolo
Great lessons! Although it didn't include one that I had to learn the hard
way, multiple times:

Your throwaway prototype will be the codebase for the project.

In my career, I've seen multiple throwaway prototypes that were hacked
together. None of them ended up in the waste bin.

"Just add X and Y and you're done". "But this is just a quick hack to show the
interface, there is nothing behind it!" "Yes, and the interface is fine, so
why start from scratch? Just add the rest".

Now I know: I never build throwaway prototypes ever again, it's always a base
for the project to come.

~~~
sadness2
How about this: Code can add value when it's a lot lower quality than we think
it needs to be. Customers will pay for even a crappy solution if you are
addressing the right problem. Don't over engineer. But also employ staff for
the explicit purpose of gradually increasing the quality of codebases which
need to continue to churn.

~~~
mgkimsal
> But also employ staff for the explicit purpose of gradually increasing the
> quality of codebases which need to continue to churn.

No one ever wants to hear that part.

~~~
sadness2
That is either a problem with leadership, or with the way the role is being
framed to them.

Perhaps this will help your case: [https://martinfowler.com/articles/is-
quality-worth-cost.html](https://martinfowler.com/articles/is-quality-worth-
cost.html)

------
kstenerud
One more point from my 20 years experience:

Keep your development environment deterministic and instantly rebuildable.

You should ideally be able to take your hard drive out, put in a new one,
install a fresh OS, run 1 script, and be completely up and running in less
than an hour.

As an added bonus, this means that you can replicate your development
environment on any machine very quickly and easily, which makes losing your
laptop during travel only an inconvenience rather than a travesty.

I've hacked together some scripts for that here [1], although I really should
convert them to ansible or something.

[1] [https://github.com/kstenerud/virtual-
builders](https://github.com/kstenerud/virtual-builders)

[https://github.com/kstenerud/ubuntu-dev-
installer](https://github.com/kstenerud/ubuntu-dev-installer)

~~~
smilliken
I've always agreed in principal, but fell short in practice until I started
using Nix[1]. Nix makes it significantly easier to pull off by verifying the
hashes of all inputs, eliminating non-determinism in builds (timestamps,
usernames, etc), eliminating dependencies on OS packages/files/etc, and
allowing you to pin to a specific snapshot of all packages. Pardon the
evangelism, I'm just a happy user.

[1] [https://nixos.org/nix/](https://nixos.org/nix/)

~~~
corrigible
I'm setting up a new laptop next week, mostly for Go/Python dev, zero front-
end requirements... What's your workflow on Nix like?

~~~
GlennS
I like to combine Nix with direnv (and direnv-mode for Emacs - there are also
similar plugins for most other editors).

Then you can `cd` into your directory and your environment appears.

The downsides are that there is a steep and long learning curve, and that your
colleagues will not understand what you've done.

I also use NixOps to actually deploy my code at the moment, but I will
probably be switching to Terraform shortly as I need some tools like AWS
autoscaling groups, which NixOps doesn't support.

These are probably helpful:

[https://nixos.wiki/wiki/Development_environment_with_nix-
she...](https://nixos.wiki/wiki/Development_environment_with_nix-shell)

[https://github.com/direnv/direnv/wiki/Nix](https://github.com/direnv/direnv/wiki/Nix)

[https://nixos.org/nixpkgs/manual/#sec-language-
go](https://nixos.org/nixpkgs/manual/#sec-language-go)

[https://nixos.org/nixpkgs/manual/#python](https://nixos.org/nixpkgs/manual/#python)

~~~
corrigible
Thanks! Just received the laptop and have allocated some resources to learning
Nix :)

------
fnord123
>ALWAYS use timezones with your dates

This is bad advice. NEVER use timezones with your dates because there is only
one timezone: UTC. Anything else is a presentation format.

Of course, if you are in a system that already committed the cardinal sin of
storing datetimes with timezones then you have to deal with it. Alas.

~~~
wvenable
If your application is future-dating items, like say clients entering
appointments in a calendar, this will be very wrong. If a customer enters an
appointment 6 months in the future, it will end up at the wrong time due to
daylight savings time.

UTC is great for timestamps (when did this event happen) but not good for
user-entered dates. You throw away useful information if you store in UTC.

~~~
dheera
No. ALWAYS store user-entered dates in UTC, and simultaneously either (a) save
the user's timezone preference or (b) use their system-specified timezone and
only do that conversion at the last step when rendering the UI, preferably
with client-side timezone functions. Also use client-side timezone functions
to provide the server with UTC dates directly.

You'll deal with DST hell on your server otherwise. Countries change their DST
and timezone policies from time to time, and you don't want to have to deal
with keeping track of that stuff or force upgrading your server for a petty
politician/timezone issue. That's the client machine's job.

Saving everything in UTC also makes comparisons, logging, abuse tracking much
easier.

~~~
stouset
Future times record _intent_ , which requires storing a datetime (sans any
kind of time zone information) and location. Past times can be stored as a UTC
timestamp.

Storing and using a time zone for points in the future is the path of madness.
If a user has an event at 2pm six months from now, they almost certainly want
that event to be at 2pm no matter what the UTC offset happens to be on that
day. This becomes even more obvious for repeating events: an event at 2pm this
week _will still be at 2pm_ six months from now, even after DST flips. And
even if my government decides to change the time zone my location is in, or
decided to opt into/out of DST.

~~~
lhorie
> If a user has an event at 2pm six months from now, they almost certainly
> want that event to be at 2pm no matter what the UTC offset happens to be on
> that day

Not necessarily. If the event is a long distance meeting that span multiple
timezones, then by definition, the event isn't going to be at 2pm for all
consumers of that data.

Location is kind of a proxy for timezone, but it makes more sense to just let
the client's OS figure the offset from UTC time than to manually manage
timezones in the server yourself.

Similarly, you let the client tell the server what timestamp "2pm two weeks
from now" is (letting it consult its OS' timezone database to figure out what
that means for the user's chosen geographic location), rather than trying to
naively add up the seconds from now to then in the server.

Saving the user's location in your DB to compute offsets on the server is
problematic because you can't always get geographic data from the client (e.g.
browsers will readily make timezone available to Javascript, but geolocation
data might be unobtainable due to permissions/privacy concerns/etc). Also,
users may move/travel, so getting geo data from side channels such as sign up
forms also could lead to discrepancies.

~~~
wvenable
If you store UTC, you automatically get the result that meeting occurs at the
same time for everyone. But you could also get the situation where, depending
on timezone and DST changes, that 2:00pm meeting doesn't occur at 2:00pm for
any of the participants!

The problem is really far more difficult and interesting than just storing all
dates as UTC as a rule.

~~~
lhorie
AFAIK that can only happen if the the government changes the DST policy (or
timezone) between the time the event was scheduled and the meeting date.
Thankfully, DST policy changes are comparatively rare.

And in actuality, even if the event doesn't happen at 2pm for the person who
got affected by the DST policy change, it'll still be scheduled at the
"expected" time for everyone else (unless they too get affected by a DST
policy change as well).

Also, most OSes are decent enough nowadays to update their timezone/DST
databases regularly so that UIs that use UTC will reflect the discrepancy in
future time intent soon after the policy change takes effect.

~~~
wvenable
If you convert a _future_ date-time to UTC using the _current_ timezone
database you've already lost. If the timezone database changes anytime from
that point forward, you cannot correctly convert that UTC date back to local
time.

You have no way of knowing, just looking at a UTC date, under what conditions
it was converted from local time.

By converting a future date to UTC, you're making the assumption that the
timezone information you have for that future date won't change from now until
that date passes.

> Thankfully, DST policy changes are comparatively rare.

"Russia switched to permanent DST from 2011 to 2014, but the move proved
unpopular...so the country switched permanently back to standard time in
2014... Florida legislature and Washington State Legislature passed bills to
enact permanent DST, pending US Congressional approval, and California, Maine,
Massachusetts, New Hampshire, and Rhode Island have introduced proposals or
commissions to that effect... Under an EU directive, from 2021 twice-yearly
adjustment of clocks will cease. Member states will have the option of
observing either standard time or summer time all year round." \-- Wikipedia

~~~
lhorie
You seem to be missing the forest for the trees. DST policy changes _are_
rare, compared to the millions of times when people schedule multi-timezone
meetings, international flights, etc. Bursts of high frequency DST policy
changes are so rare that they get articles written about them.

> you cannot correctly convert that UTC date back to local time.

Of course you can. It may not be the same local clock time with most
implementations, sure, but it will still correctly point to the same physical
time relative to the local times of everywhere else on Earth. It would be
physically impossible to retroactively change the time of the meeting to
account for the the DST policy change and maintain the same clock time for
participants in other timezones, so something's gotta give.

It's also not true that you can't recover the intended time after a DST policy
change. Although it's monumentally laborious, it's definitely possible: you
can keep track of the historical changes in the timezone database and work
backwards from the UTC date and the date of that record change.

> By converting a future date to UTC, you're making the assumption that the
> timezone information you have for that future date won't change from now
> until that date passes.

Sort of. The assumption there is that the time delta between now and then is
smaller than the delta between a notice of policy change and the time it goes
into effect. It is, however, technically an engineering trade-off. The trade-
off here is that you can have a far simpler and almost-always correct
implementation by taking a tiny risk of a discrepancy between intent and
actual local time in the extremely rare occasion that a policy changes without
advance notice. Responsible governments will typically give plenty of advance
notice so timezone databases can be updated and clients can then correctly
calculate future offsets since they will know how to account for the policy
change.

You could certainly forego UTC time and make an engineering decision of
storing local clock time if your application matches a very strict set of
timezone boxing requirements, but in my experience, it's more common that
people pick this approach out of ignorance than careful analysis, and in some
cases, it gets nasty to support when the requirements change (e.g. as soon as
you have a new sales team office on the other side of the country)

~~~
wvenable
Why go through all this _just_ to follow the rule of storing UTC in the
database? At what point does it just become the _wrong_ thing to do?

Storing local time and timezone in the database for future dates is _easy_. It
has none of these issues. It produces the correct results that people expect.
You can convert to UTC at anytime.

~~~
lhorie
Storing local time is fine if you and your server never ever leave your
timezone. You can even get away with it for a long time. If your logic does
ever require timezone conversions down the line though, it will most likely
surface some nasty problems. And when I say nasty, I mean it in the will-
leave-a-battle-scar-that-your-future-graybeard-self-will-warn-younguns-about
sense.

The thing with the UTC advice and those who don't listen to it is that people
often don't realize that both DST policy changes and factors that increase the
number of applicable timezones in a business are rare to begin with. They
often think that that storing local time is safe because they never had to
truly deal with the complexities of representing physical time.

But DST policy changes are much rarer than factors that fuck up local time
based implementations, and issues can be fixed without huge schema changes and
downtimes. Plus it's less likely you will actually need to fix them since
higher-ups won't be able to grasp the issue, whereas a local time bug fix will
often come as a demand from some higher-up suit who flew out of state.

~~~
wvenable
I've said all over this thead to store local time _and the associated
timezone_. You do that and no nasty problems can happen. And you are correctly
storing the time that the user intended and not the time it might be converted
into.

~~~
lhorie
If you're storing the timezone (and by that I assume you're doing so
unambiguously, because offset alone doesn't tell you hemisphere/DST rules, and
timezone names are not unique), then you're effectively using UTC, albeit
storing it in a non-normalized format.

The flaw in your logic is in assumption that "intended time" is always fixed
relative to one user's clock time. For international events, intended time is
physical time. If it's a friday and I verbally arrange a meeting with someone
in Sao Paulo at 3pm BST the following monday and that translates to 11am PST
today for me at San Francisco, but if there's a DST change for me over the
weekend, the intended time is still 3pm BST, not 11am PST, because in such
cases, the general logical expectation is that whoever is getting a time
change is the one who should be on top of it.

In either case, you'd be able to recover "intended time" (for any meaning of
the term) 99.9999% of the time regardless of whether you used UTC or local
time+timezone. If there was indeed an unlikely event of an unannounced DST
policy change, then whether intended time is wrong depends on who's affected.
If it's an international meeting and intended time means physical time, then
local time+timezone does not capture true intended time. Likewise, if intended
time means local time for a single user, UTC will be the wrong one. But as I
said, this case is vanishingly unlikely. A lot of code doesn't handle integer
overflows correctly because they are unlikely too and the world is still
humming along just fine.

------
maxxxxx
One thing I have learned in almost 30 years of SW development: work somewhere
where the CEO has at least a basic idea of your work and sees value in your
work, not just cost. Work somewhere where people care for the craft and it’s
not only about “business” goals.

~~~
1PlayerOne
Can you give examples of companies that fit your description?

~~~
maxxxxx
Apple under Jobs, Google, Facebook, MS under Gates, lots of smaller companies.
Probably the ones where a founder is still CEO.

~~~
savrajsingh
And the founder was a software engineer

------
physicles
> Keep a record of "stupid errors that took me more than 1 hour to solve"

I’ve been doing this for the last three years, but electronically, and it’s
amazing. In my home folder there’s a giant text file that’s like my personal
log file. It gets a new line at least once an hour, when cron pops up a box
and I type in what I’m working on. And for those times when I’m working on
stuff that’s perplexing, I write to it as a stream of consciousness — “tried
this, didn’t work” “wonder if it could be due to xyz” “yep, that was it.”

I’ve used it a few dozen times to either look up how I solved a weird problem,
or why I decided to do something a certain way. I’ve even built pie charts of
how I spent my time in the last quarter by sampling it Monte Carlo style.

~~~
unimpressive
Reminds me of:
[http://messymatters.com/tagtime/](http://messymatters.com/tagtime/)

Which is a pretty well formed solution in this vein, unfortunately written in
Perl.

~~~
dsmithn
any experience with newtagtime.com ?

------
strken
The description of cognitive dissonance is wrong. It's not holding two things
in your head at once[0], it's the feeling you get when you believe two
contradictory things.

[0] that's closer to
[https://en.wikipedia.org/wiki/Working_memory](https://en.wikipedia.org/wiki/Working_memory)

~~~
simplify
Yes, and it's also not as severe or "a sign of being stupid" as many think.
Cognitive dissonance occurs in situations as simple as walking to a store and
discovering the store is closed.

~~~
1123581321
It’s also used in marketing to describe what happens when a buyer’s
expectations are not met by the product, and is entirely used to fault the
seller and the seller’s marketing.

------
robodale
> 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 cannot agree more. (18 years developer here). On those days I just kept
pushing, I mostly ended up correcting my poorly-created code the next day.
When you know you're done, be done, or at least switch to the lowest mental
energy task you can do. Sometimes staring into the screen is that lowest
mental task.

~~~
diggan
> Sometimes staring into the screen is that lowest mental task

For the sake of your eyes (and perhaps well-being), don't just stare into the
screen without doing anything. If you have a free moment, take your eyes off
the screen and look out the window (or at nearest wall if you have none)

~~~
WhompingWindows
This is when I take a slow walk around the building or outside for a few
minutes.

------
bobm_db
I really like this one:

> Documentation is a love letter to your future self

This seems like a brilliant way of transcending what feels like a drudge-job,
making it into something really meaningful.

~~~
snarf21
Not that documentation is bad but it is rarely correct. Now you have to spend
time trying to match documentation that doesn't match the code you see.

I always prefer a different approach. There is no need to document _what_ a
function does, that is self evident from stepping through it. The thing that
should be included in the code is the _WHY_ , meaning why does this function
even need to exist and how does it fit into the architecture?

Now an accurate high level architecture diagram and function raison d'etre
telling you everything you need to know with much less risk of not having an
accurate and up-to-date documentation process.

~~~
brianpgordon
> Not that documentation is bad but it is rarely correct. Now you have to
> spend time trying to match documentation that doesn't match the code you
> see.

It depends on what you mean by documentation. If you mean articles on
Confluence or whatever, yeah, that's hopeless. If you mean comments in the
code, I couldn't disagree more.

I don't buy the common argument that comments easily become inconsistent with
the code. I can't imagine changing a line of code and completely ignoring the
comment above it, and I'd definitely require a fix if I saw someone else do
that in a code review. That's just a ridiculous level of sloppiness.

> There is no need to document what a function does, that is self evident from
> stepping through it.

I'm afraid I disagree here too, though again conditionally depending on what
you mean. Straightforward logic shouldn't be littered with superfluous
comments. But on the other extreme, the fast invsqrt algorithm needs more of a
comment than the canonical "what the fuck?"

Yes, given weeks of head scratching any arbitrarily-dense block of code will
release its secrets, but that's not good engineering. Anyone can build a
bridge by dumping a hundred billion dollars of concrete into the bay until
it's dry; good engineering is doing it cost-effectively. A well-placed comment
in some non-obvious logic can save time for a developer (likely you) every
time they interact with it.

------
sethammons
> People get pissed/annoyed about code/architecture because they care

> ... Yeah, you don't like that hushed solution 'cause you care" was one of
> the nicest things someone told about myself.

This is very similar to a foundation that we repeat at work. Everyone is
trying to make things better, we might not agree on the how, but we must agree
on the intent. When you assume good intent, conversations go much, much
better. It can be a great way to frame disagreements. "We are disagreeing here
because we care."

------
shakna
> For a long time, I kept a simple programming rule: The language I'm playing
> at home should not be the same language I'm using at work. This allowed me
> to learn new things that later I applied in the work codebase.

Though I've found this helpful in expanding what I can do, or how well I can
do it, what I find using a different language at home than at work does best
is prevent burnout.

I can be reckless, and play with the language, because I don't have the tired
patterns of my day trying to rigidly enforce best practices and cover all edge
cases.

My mind can relax and do the wrong thing if I want, and I don't have the
autoformatter or linter in mind. I don't have a dozen rules that will spit red
errors if I step out of line.

Those things are all good for work. The application must be solid, and the
user deserves stability.

But when I'm hacking together a game for myself that I never intend to
release, I just want to experience the fun side of programming without the
hard part.

------
vmlinuz
Haven't read all of these yet, but a couple stood out as matching recent
experiences...

"Future thinking is future trashing": we had a good engineer with a bad
tendency to write complex generic code when simpler specific code would work.
In one case, he took a data structure and wrote a graph implementation on top
of it - it was only ever used once, by him, and I recently rewrote it in about
15 lines of structure-specific code. In another case, he wrote a query builder
to make it easy to join data from a new service with our legacy DB, which
makes the high-level code simple, but the full implementation much more
complicated, and means incoming engineers can't obviously see what
implementation to use. His last week at the company was... last week.

"Understand and stay way of cargo cult": Today, I was reviewing someone's
code, when I saw a language construct I didn't recognize, and which made no
sense to me. I asked him what it was meant to do, and if he could give me a
link to somewhere which described the construct - he said no, he'd mostly just
copied it from someone else's code. I asked 'someone else', and he pretty much
said it was useless, he's worked in so many languages that he gets confused
about language idiom/construct sometimes, he writes his code like he'd write a
letter to a friend, and it wasn't like anyone was reviewing his code at the
time anyway. I said I wasn't going to take his stuff out of the existing
codebase, but I wouldn't let anyone copy his useless code in future, and
linked him to [https://blog.codinghorror.com/coding-for-violent-
psychopaths...](https://blog.codinghorror.com/coding-for-violent-psychopaths/)

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

D not only has it as a library, it's a core feature of the language. As
incongruous as it sounds, builtin unittests have been utterly transformative
to how D code is written. Built in documentation generation has had the same
effect.

------
edw519
Excellent post! Thank you, Julio!

This could make great bulletin board material in some other format, but
there's just too much here for that.

I started my reply by cutting and pasting the gems with my little follow-up,
but quickly realized that would take all day. There just so much good stuff
here. So instead I narrowed it down to just one:

 _Code is humans to read. ALWAYS. Optimization is what compilers do. So find a
smarted way to explain what you 're trying to do (in code) instead of using
shorter words._

This really strikes a nerve because in 40 years of programming, most of my
problems have not been with building code myself, but with trying to figure
out what my predecessor had written. I bet I've wasted 20 years of my life
refactoring the crap of others who would have benefited greatly by reading
posts like this one first.

There's lots to think about. Lots to smile and agree with. And lots to
disagree with (OP reminds me a little of myself, a caveman who often claims to
know "the one right way" :-)

If you don't have time to read this now, bookmark it and come back later. One
good idea from this can make a big difference.

~~~
maxxxxx
"This really strikes a nerve because in 40 years of programming, most of my
problems have not been with building code myself, but with trying to figure
out what my predecessor had written. I bet I've wasted 20 years of my life
refactoring the crap of others who would have benefited greatly by reading
posts like this one first."

I also curse a lot about code my predecessors have written but I bet my
successors will curse at my code too :-)

------
blablabla123
> Learn to recognize toxic people; stay away from them

> You'll find people that, even if they don't small talk you, they will bad
> mouth everything else -- even some other people -- openly.

So true. It has happened to me so often - actually at most brown-field
projects - that when I get onboarded, someone is telling me in which bad state
this and that is, even how bad this and that person is supposed to be.
Although I tend to be optimistic, when the project seems to be nicely
challenging at first (not only from the code), I somehow take over this
negative attitude for the rest of the project.

I think people should be able to informally talk about projects, even gossip
sometimes. But it has its limits, especially team/project leads should IMHO be
really careful about taking negative stances on certain projects/people/teams.
In fact the limits to mobbing are fluid.

For me this is the single biggest annoyance in any job.

~~~
ShamelessC
> So true. It has happened to me so often - actually at most brown-field
> projects - that when I get onboarded, someone is telling me in which bad
> state this and that is, even how bad this and that person is supposed to be.

I certainly don't want to encourage any sort of toxicity like this. I will
say, however, that I recently started a new job. People haven't been
particularly welcoming so far but I finally managed to bond with someone over
a discussion of the skill gap that exists between some of the employees and
the lack of quality code produced by a few of the stubborn veterans.

It wasn't just a bonding experience either. I was able to learn more about the
nature of the organization and how I can help to improve things in the future.
It also had a calming effect because I was originally pretty intimidated by
some of these veterans but now I know they're just flawed humans like the rest
of us.

~~~
blablabla123
Pretty close to that was also my last experience. But having seen that before,
I wasn't intimidated by all these accomplishments. In fact running a linter
objectively showed the overall low code and design quality.

Anyhow, complaining doesn't help but trying out things and gradually
improving. In fact it's possibly even a good opportunity because nobody else
wants to touch such code so there's room for creativity (of course within the
limits that the stakeholders are fine with).

------
js8
I think the most difficult lesson is that good software is like cheese or
wine. You have to let it age, to find the proper way to approach the problem;
adding more developers will not make the process faster.

------
peterwwillis
Something I learned: K.I.S.S. is the most important principle in technology,
but keeping technology _simple_ while still adding value and utility is
_really hard_.

Example: how can we improve on the design of the bicycle, but keep it
_simple_? You could add expensive composite materials, design a new gearing
mechanism, create new complex geometries based on wind tunnel experiments...
all of those are more advanced, but complex; _simple_ is a tough nut to crack.

------
chamilto
> When you're designing a function, you may be tempted to add a flag. 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.

What about in languages with named parameters? I think keyword arguments solve
this issue pretty well.

~~~
EdgarVerona
This one's definitely a weakness I still have. I read that and thought "yeah,
I do this in a lot of places and often end up regretting it."

I think named parameters don't actually help, because the underlying problem
with the boolean (aside from the mystery about what it means when looking at
code calling it) is that it implies a potential separate "mode" of operation
for the function. That the single function might actually serve two different
purposes. It doesn't _always_ imply that I imagine, but it's pretty likely.

I'm guilty of this in my code, and I know that my code quality has suffered
for it - for some reason, the way he put it in this article gave me a moment
of introspection there, and it's something I'm going to try and take away from
it and improve my own code with in the future.

~~~
tabtab
However, using multiple names can create a combinatorial explosion. If you
have 3 Boolean parameters, then you need up to 8 functions. I think named
parameters are probably a better option.

~~~
pavon
Only if you really need all combinations of those options. The flip-side is
true as well - multiple booleans creates a combinatorial explosion of corner
cases that you have to test for. I have definitely been guilty of adding flags
to functions that really should have been separate functions, and being bit by
permutations that were not tested. Some of which we couldn't even decide what
the proper behavior ought to be when we went back to fix the function.

~~~
tabtab
Wouldn't those same combos need to be tested regardless of which interface
technique is used? If there are 8 variations then there are 8 variations
regardless of the name-centric or flag-centric approach.

Note that sometimes I add an optional boolean parameter to avoid breaking
existing method calls. The default of the new optional flag is the original
behavior. It's a case being backward compatible versus changing more code in
order to fit a revamped interface.

------
therufa
> Data flows beat patterns (This is personal opinion) When you understand how
> the data must flow in your code, you'll end up with better code than if you
> applied a bunch of design patterns.

this one is priceless.. I had so many arguments in my past with cocky devs
about this topic. Don't get me wrong, patterns are good, but it's only a small
part of coding. Also patterns aren't there to follow them as they where the
law, but bend them to your needs. One doesn't even necessary needs to be
implemented to 100% as it is in the "book". So yes, this part of the post i'm
very fond of, it's really wise.

~~~
cgrealy
I cringe whenever I hear someone talking about "what pattern to use here" (I
cry if the answer is "singleton").

Patterns are a way of _describing_ code. They are meant as a common language
to use when explaining a codebase to another dev. If you are writing code with
the express intention of "using X pattern", you're doing it backwards.

------
Const-me
Most of them are awesome, but some are questionable.

> don't add flags/Boolean parameters to your functions.

Many languages have strongly-typed enums for them, makes calling code
readable, e.g. getUserMessages( user, eMessageFlags.Full ). Especially useful
for more than 1 flags.

> ALWAYS use timezones with your dates

UTC usually does the trick?

> UTF8 won the encoding wars

UTF8 used on the web a lot, but OP seems to be generalizing their experience
over all programming, not just web development.

> when your code is in production, you can't run your favorite debugger.

No but I can run my second favorite, WinDBG. A crash dump .dmp file is often
more useful for fixing the bug than any reasonable amount of logging.

> Optimization is for compilers

Compilers are not that smart. They never change RAM layout. I've recently
improved C++ project performance by an order of magnitude replacing
std::vector everywhere with std::array (the size was fixed, and known at
compile time). Before I did, profiler showed 35% time used in malloc/free, but
the cost of vectors was much more because RAM latency.

------
agentultra
Type systems exist on a spectrum. A rich type system is better at documenting
your code than comments are if the compiler and it's type system are sound:
the comments can be wrong, the types cannot.

A sufficiently expressive type system can check your logic for you. Push as
much of your propositions and predicates into the types as possible.

Data types are better than exceptions, return codes, etc for the majority of
unexceptional cases. Conditions and restarts are better than exceptions.
Sometimes crashing is not an option when your computer is flying in the air
with a human.

In addition to specifications -- often a bunch of boxes with arrows will not
be sufficient. There are a range of tools to check your designs from automated
model checking to interactive proofs. When the problem is difficult to
understand on its own then the solution is probably even harder -- a good sign
you should consider using one of these tools to make the problem clear and
guide you to the right solution.

~~~
JamesBarney
I agree with everything you said, except >: the comments can be wrong, the
types cannot. If you're arguing comments are valuable but type constraints are
a better way to express requirements than I agree and sorry for the long post.
But I've seen a lot of comment hate on hackernews and I feel like I have to
defend comments.

I really think comments can be especially valuable. They're valuable when
reviewing code because they tell the reader what the developer was attempting
to do, not necessarily what they did.

Not everything is expressible in every type system. Even something as simple
as "this reference can't be null" isn't expressible is many languages.

Comments are easier to read than type names, a sentence is much more
expressive than code. Especially code that is tricky due to performance or
domain complexity.

Type and method names can rot too. If you have a method that starts with get
that someone adds a little bit of updating logic to making the method name
misleading.

And comments can be kept up to date easily by just adding an item to your code
review checklist "Ensure comments match code".

~~~
brianpgordon
I'm on team comments too. The article puts it well:

> When you start the code by writing the documentation, you're actually making
> a contract (probably with your future self): I'm saying this function does
> this and this is what it does.

> If later you find out that the code doesn't match the documentation, you
> have a code problem, not a documentation problem.

"Self-documenting code" is, in my opinion, a silly canard. Maybe it's
appropriate for some brain dead glue logic or frontend boilerplate, but any
time you really need to think about what the code is doing, a comment will be
immensely helpful. I don't at all buy the argument that comments too easily
get stale. They won't get stale if you maintain them.

~~~
gregmac
Honestly, there's very little code where comments on the function calls can't
be helpful. Let's use GetUserDetails(userId)

A comment like "gets the details about a user" is of course a waste of time.

What I like to see is everything about using it, without having to read the
code. For example: "Gets details for a user, based on the Service.CurrentUser
access rights. If userId doesn't exist, is not available valid id, or the
current user diesn't have permission, throws exception. Note this can return
data for disabled and deleted users, so if used for non-admin functionality,
you should check the State property. Due to caching, this might not show
changes made in another session in the past couple minutes."

The lack of a check for disabled status (for example) won't be obvious if I'm
not actively thinking about checking disabled status. That few seconds of a
comment can easily prevent a bug when someone consumes this - especially if
they didn't write this API or are new to the cosebase (maybe they don't even
know disabled users are a thing).

Sometimes when you write these types of comments you realize your naming is
bad, or that there's an API change that would make it easier to avoid a
mistake - such as having separate GetActiveUserDetails() and
AdminGetUserDetails().

I think about this during vice reviews, too: I've often asked for comments to
be added on existing methods being _consumed_ (but not modified in the code
I'm reviewing) because the new use made me wonder about how something would be
handled and I had to go read a couple levels deep to figure it out.

~~~
agentultra
Agree. And in a richly-typed language you can be more specific:

    
    
        getUserDetails :: UserID -> DBAction (Maybe AnyUser)
    

This type says a lot already. The definition of `AnyUser` would specify the
parameters that the comment only says in prose. Or we could be more specific

    
    
        getActiveUserDetails :: UserID -> DBAction (Maybe ActiveUser)
    

We could also include the authorization specification in the type constraints
on the `DBAction`:

    
    
        getUserDetails :: (DBAction m, HasAdminContext m) => UserID -> m (Maybe AnyUser)
    

This is what I mean when I say that a rich type system can enforce a lot of
what you might traditionally use comments for in a weaker type system. Now my
hypothetical comment might highlight some salient points about this function,
such as _performs N queries to validate all of the available authorization
contexts, use with a user role that has a limited number of contexts for best
performance_.

------
SamuelAdams
For as much grief as Medium gets, it sure would help blogs like this. The font
color is a poor choice considering the background, the font itself isn't that
common for reading long text forms.

Not saying Medium is perfect, but it does help standardize the display of all
these random, one-off blogs.

~~~
jp_sc
I strongly disagree. This is like saying that every store everywhere should be
a Walmart so one doesn't have to adapt to the organization of other stores.

In my opinion, the style of the blog is as important as the content because we
are dealing with the self-expression of a person here. The colors and fonts
were conscious decisions, and presenting the data in the most efficient and
standardized form is not the first priority.

(Also calling it a "one-off" blog is kind of mean. Sure it might the first and
the last time you ever read a post there, but that blog has more than one
hundred posts, so it's not a "one-off" for the author.)

~~~
astine
Right. The font at least is a monospace font common in terminals. It's a clear
conscious design decision that technical readers are likely to pick up on.

------
chewxy
> A long time ago, a small language made the rounds by not evaluating
> expressions when they appeared, but when they were needed.

> Lisp did this a long time ago, and now most languages are getting it too.

I'm not sure if this is correct. You can probably write a macro for lazy eval,
but by default lisp is a CBV almost-lambda calculus.

Or you could pass around quoted things, I guess

~~~
phoe-krk
F-expressions were a thing back in the day.
[https://en.wikipedia.org/wiki/Fexpr](https://en.wikipedia.org/wiki/Fexpr)

Possibly the closest thing available in Common Lisp are Lisp macros - they
receive their arguments unevaluated, and are free to utilize that input for
further computation.

~~~
chewxy
TIL. I only came across fexprs briefly on LtU once and never looked more into
it. Thank you.

------
nunez
> Understand and stay way of cargo cult. "Cargo cult" is the idea that, if
> someone else did, so can we. Most of the time, cargo cult is simply an "easy
> way out" of a problem: Why would we think about how to properly store our
> users if X did that?

_cries in kubernetes_

------
coryfklein
OMG I _never_ have encountered this idea for function deprecation in libraries
but I think it is hilarious:

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

This idea is sure to waste lots of people time, lead to angry emails, and is
probably _worse_ than just changing the function signature in-place without
any deprecation schedule at all.

~~~
brianpgordon
[https://twitter.com/johnregehr/status/920691341738123264](https://twitter.com/johnregehr/status/920691341738123264)

------
GuB-42
That's interesting because even if I agree with most technical points, I went
the opposite way for most of the general points.

The big one is: start with the specs, then code. I used to think that, wasting
time making plans for an end result that ends up completely different. Now, I
prefer to start coding right away. Anything goes, it just has to be code.

Why code? That's because it keeps my feet on the ground. And because it is the
early phase, I can still change things. Chances are that most of that early
code will be scrapped, maybe several times. No big deal, I may have lost a few
days of coding, but I have a much clearer view than if I spent that time
writing specs.

As for the documentation vs code, I spend all my experience points into
reading and understanding code. I sometimes even force myself not to read the
documentation, going straight for the code instead. Comments are lies. For me,
documentation will always be an afterthought, for others. For me, I have the
code, and if I have trouble reading it later, that's because it is bad code,
not because it lacks documentation, and I try to learn my lesson, and maybe
rewrite the offending parts.

For the social part (code of conduct, micro aggression, etc...), my strategy
when things become unpleasant is to go technical. The bigger the asshole
facing me is, the more deeply technical I go. Here two things can happen.
Either the asshole is incompetent, and because he doesn't want to look too
much like a fool, he usually leaves me alone. The other is that the asshole
turns out to be really good, and in that case, the interaction may not turn
out to be the most pleasant, but at least, there is something to learn.

------
wiz21c
Well mines : Unicode is trickier than you think, there are some date-time that
don't exist, transactions are trickier than one (usually) think, don't use
floating point for money (because many people don't understand floating
point), a quick fix always introduces another bug, any contract (WSDL, json-
schem, whatever) is full of holes, integrate with others first, legacy
database are _full_ of bad/unexpected data,...

------
bluejekyll
> Unit tests are good, integration tests are gooder

I would personally rephrase this as unit tests are necessary, as are
integration tests, as are end-to-end tests.

The testing pyramid: write most of your tests as unit tests, then write a
bunch of integration tests, and finally throw in a few end-to-end tests.

> Tests make better APIs

Yes! This is such an important point. A perfect example I love is that globals
and even thread-local values make testing hard, and that’s reason enough to
avoid using them.

> Make tests that you know how to run on the command line

Again great advice. I would also add, get your tests running as early as
feasible. This saves you rewriting APIs and other decisions that might make
testing, even deployment (which you need for end-to-end tests), significantly
harder.

Anyway, this entire set is a great read, and I agree with much, but not all.

~~~
sadness2
I'm with Julio on this one. The triangle ought to be a diamond, with the
integration tests being the fattest part. This is a level where tests can be
at the component level, where they can correspond to specifications which are
comprehensible to stakeholders (which helps keep developers accountable to
focus test effort on things that really matter), and where refactoring of
component internals do not require corresponding refactors of test code. This
creates challenges, 90% of which are resolved by applying the same good
development practices (re-use, abstraction, performance) to your test code
that one is accustomed to applying to application code.

~~~
sbov
Many people consider module-level tests to be unit tests.

Integration tests would mix multiple modules.

Depending upon the specifics of your application, unit tests at a lower level
than the module level may be suitable. E.g. complicated algorithms a module
might internally rely upon.

Our language around testing is missing a slot. We have unit, integration, and
end-to-end. Should be unit, module, multi-module, and end-to-end. Depending
upon the specifics of the application, you might have more unit or module
level tests.

~~~
sadness2
> unit, module, multi-module, and end-to-end.

A great semantic insight.

I still prefer to test complicated algorithms via the external interface. It
does end up requiring some scaffolding code for generating inputs based on
test cases, but code is for automating repetitive tasks, and they're pretty
fast these days, so it's rarely a problem, even for many thousands of test
variations. It enables you to test the interactions between your various unit-
level variations, which is more likely to catch errors. Again, it also helps
make your tests visible, along with the edge cases you're testing, and many
times developers working at the unit test level are way more thorough than
they actually need to be. Couple this with efficiencies around refactoring and
it can actually save time.

------
dheera
> "Don't use Booleans as parameters"

... except I think it's perfectly fine in languages that support named
parameters. In fact this isn't even specific to booleans. Say we're using
Python,

    
    
        draw_rect(width = 5, height = 10, top = 5, left = 6)
    

is much more readable than

    
    
        draw_rect(5, 10, 5, 6)

~~~
turingbike
Named parameters _might_ be my favorite part of Python. I don't understand why
every language doesn't have them.

~~~
astrange
Objective-C and Swift also. Though my favorite implementation is AVISynth, the
best language and most interesting execution model nobody uses.

------
pier25
> Solve the problem you have right now. Then solve the next one. And the next
> one.

Amen to that.

------
WalterBright
> I tried to become "paperless" many times. At some point, I did keep the
> papers away, but in the very end, it really do help to have a small notebook
> and a pen right next to you write that damn URL you need to send the data.

I thought I was old-fashioned because I keep one of those spiral notebooks on
my desk. It's invaluable. I've never been able to take quick notes using a
computer.

When I fill up the notebook, I run it through the scanner, toss it and get
another.

------
joaobeno
Happy to see some things I learned the hard way with 15 years... Man, I wish I
had this level of knowledge when I was 20...

------
lifeisstillgood
If a function description includes an "and", it's wrong

yes :-)

------
amelius
> Spec first, then code

I wish I could make my boss understand the importance of this one.

In his mind, the best coding strategy is:

1\. build prototype

2\. add feature

3\. goto 2.

------
drderidder
I like the point "The function documentation is its contract". I experimented
using JSDoc instead of TypeScript on a recent javascript project and have
found it so enjoyable, partly for that reason.

------
jupake
Fastastic! This is so deliciously language/framework/editor/OS agnotic.

Nothing quite like experience to make you wish you could go back in time, with
all your current knowlege, and take over the world.

------
cgrealy
> Always use a Version Control System Please tell me no-one is writing
> anything without a VCS in 2019.

> One commit per change Amen, brother.

> You're responsible for the use of your code That's a tough one. Code is a
> tool, like any other. If you're making a gun, then you have to accept that
> your product will kill people. But if you're making a hammer, it's not
> really fair to hold you responsible when someone goes postal with it.

> Learn from your troubles This. So much this. If you're not doing this... the
> rest is worthless.

------
hamburglar
This is a great, great list, but what the heck is this:

> This is what most people get wrong about adding booleans to check the number
> of True values.

This appears in two different sections. Who are all these people adding
booleans as if they're integers? It's like someone wrote a whole treasure
trove of sensible life advice that included "this is why most people have
trouble when they use a dry-erase marker as a toothpick."

~~~
mixmastamyk
Looks like a weird side effect of Javascript being exploited by the
unscrupulous.

------
WalterBright
Most of these sort of lists are crap. But this one is good. I'm amazed I agree
with nearly all of it. (>40 years in the business!)

------
JoeAltmaier
Good stuff, until it broke down into coding style.

------
rurp
I'm curious about the "Don't use Booleans as parameters" point. I primarily
write Python and like to use keyword arguments in these cases. So the function
call would look like

    
    
      get_message(full_message=True)
    

Is this an OK pattern for Python but not some other languages, or am I writing
more confusing code than I had realized?

~~~
ndiscussion
That's exactly what the post is warning against. It's really hard to explain
without a concrete example, but basically, I would make two functions:

get_full_message and get_partial_message or something like that

~~~
ShamelessC
Is it safe to say that using named keyword arguments mitigates this issue?

------
cgrealy
A lot of people in this thread have argued that named parameters mean you can
add bool params to functions.

e.g. getUserMessage(userId, fullMessage=true)

While that can help, I'd still argue against it unless your language has some
mechanism to force use of name. Because if you _can_ omit the name (i.e.
getUserMessage(userId, true)) then someone _will_ omit the name.

------
WalterBright
> You can create the new functions and mark the current one as deprecated,
> either by documentation or by some code feature.

Another D feature that is so simple in concept but miraculous in its effects -
a "deprecated" attribute that can be applied to declarations. It enables
plenty of warning given to users before it is removed.

~~~
astrange
Well sure, but GCC and co have that too.

It's a pretty good counter to people who think you should always use -Werror.
One wrong deprecation could take you a month to fix!

------
lliamander
> Also, remember that most people that are against CoCs are the ones that want
> to be able to call names on everyone.

There's a XKCD comic which points out how people who claim to "hate drama"
tend to be the most dramatic people[1].

That's how I tend to perceive projects that put a lot of emphasis on their
CoC. The more they emphasize it, the more I expect pettiness, vindictiveness,
and bad faith.

[1][https://xkcd.com/1124/](https://xkcd.com/1124/)

~~~
astrange
It's not surprising that a large or corporate project would need an HR
department, which is what the CoC is. Anyone who objects to that probably has
never worked at a large company, or used a forum with no mods.

But the important thing isn't the rules, it's that there's someone to enforce
them. Ideally, the rules exist to constrain them, not you.

~~~
lliamander
Whatever you may think about how a CoC ought to work, my observation about
they work in practice (and the people who push them) remains the same.

> Anyone who objects to that probably has never worked at a large company.

I can assure you that's false.

------
kgwxd
Just happened to follow Julio on Mastodon yesterday, he commented on this
thread there:
[https://functional.cafe/@juliobiason/102292722895801015](https://functional.cafe/@juliobiason/102292722895801015)

------
dorusr
> If a function description includes an "and", it's wrong

Unless you have procedures.

Also, the 'don't add boolean switches' doesn't count for Powershell, as you
can use named switches that are false if not added. For other languages you
could add constants

------
rainhacker
> Beware of micro-aggressions

> Toxic/micro-aggressors are only fixable if they are YOU

This is a good thing to keep in mind. But the article doesn't provide any
suggestion when you are a victim. Anyone has tip(s) to deal with a micro-
agressor?

~~~
davidjnelson
The authors recommendation seemed to be to change jobs.

------
pmarreck
it’s a pretty long read... clearly the author did not learn brevity in his
coding, or that his blog’s color scheme does not contribute to readability

also re: Gherkin: Just don’t cucumber. If you focus on integration test
technology like Cucumber or headless browser drivers, they will absolutely
balloon your test suite runtime and it is partly because they are extremely
inefficient (testing the same things over and over and over again such as “set
up a logged-in user with 3 assets” for 90% of the test cases in the suite).
Avoid. Maybe not entirely, but in large part.

~~~
_asummers
> it’s a pretty long read... clearly the author did not learn brevity in his
> coding, or that his blog’s color scheme does not contribute to readability

It's also riddled with typos which made reading it hard. There's some wisdom
in there, but it was clearly written in an editor without spell/grammar
checks. Maybe there should have been a section about rereading your own work
before sending to your team/prod? =)

~~~
reallydontask
I would guess that based on name and link to a book, he wrote, in Portuguese,
that the chap's first language is not English

~~~
Kreotiko
As a fellow not native speaker I don’t think this should matter the moment you
choose to use English. Also, personally, it wasn’t just the grammar that put
me off but also all the aggressive colloquialism used.

~~~
reallydontask
I give non-native speakers a lot more leeway for typos, grammar and odd
expressions for, hopefully, obvious reasons.

A lot of people use English as it's the language of _tech_ , which means that
the potential audience is significantly larger.

------
RocketSyntax
"Types say what you data is" Are you saying to first check the type of your
data before running a boolean so that you can catch high level things that are
Null when they should be char or int?

------
MichaelMoser123
What i learned: keep a log of problems solved and things that slowed me down.
Helps me to be more aware of what I am doing, hope it will keep me from
shooting at my foot, at least occasionally.

------
herodotus
> Spec first, then code

> If you don't know what you're trying to solve, you don't know what to code.

I like this, but I would add "Always read the spec before you code".

------
graphememes
The only one I have an issue with is

> Future thinking is future trashing

It really depends on what you're building, and how long term the project will
be around.

Git vs Javascript Frameworks

------
thieving_magpie
I enjoyed reading that. I recognized a lot of things I used to do and a lot of
things I currently do. It's nice to gain perspective on that.

------
Raphmedia
The sections on work relations are golden. If there is anything you should
remember after reading this, it is these points.

------
luord
I agree... on the actual technical points. Particularly the bits about design
patterns and thinking about the users first.

------
AtlasBarfed
Can I get some further commentary from people with 50 years of experience in
software development?

~~~
astrange
Here's Alan Kay.

[https://news.ycombinator.com/item?id=11939851](https://news.ycombinator.com/item?id=11939851)

~~~
stebann
Thanks!

------
neves
Some of the things depend on the language

getUserMessage(userId, True)

in Python can become

getUserMessage(userId, getUserMessagesFull=True)

~~~
prometheus76
What happens if I set it to False, and what happens if I don't set it? Does it
default to sending a summary, or does it send a null? I don't see how having
named parameters eliminates the problem OP is talking about.

------
tuananh
> Don't use Booleans as parameters

ah, he must have forgotten about named parameter

~~~
falcolas
Even with named parameters, unless exquisitely named, it can still be more
confusing (and less flexible) than constants.

~~~
tuananh
constant as in enum? in that case, enum gotta be name exquisitely as well.

------
m3kw9
#Future thinking is future trashing

------
Doubl
I don't see much wrong with true or false as arguments. Beats having two
functions instead of one which is his preference.

------
surajs
This is awesome, thank you!

------
raveenb
good one!

------
majewsky
Things I learnt the hard way reading this article: Headlines with `font-size:
1em` make a large article unnecessarily hard to skim.

~~~
LoSboccacc
1em can not be at fault it's literally 'whatever font size parent has' the
culprit is somewhere else.

~~~
proaralyst
Which is specifically the body text size, no?

~~~
LoSboccacc
yeah, but that's the issue: hs and ps are both coded to the same size which
happens to be 1rem

i.e. 1rem hs would be ok if you'd had 0.8rem ps, and conversely you'd have the
same issue if both were coded to be 2rem

------
molteanu
> Spec first, then code

and then later

> Be ready to throw your code away

Then why would it be useful to have the spec first if you're expecting to
throw it away anyway?

Better combine this advice,

> Write steps as comments

with a higher level language, and write code instead of comments, and have fun
developing what you think is the product you need. You'll see plenty of corner
cases, learn tons of new stuff about your domain that you didn't even think
existed and then you'll want to start over since many of your initial
assumptions will be wrong anyway by that time or you'll see new extra features
that you couldn't have possibly discovered without actually having a toy
product to play with and try new ideas with.

Tests? No, definitely not on the first run. Maybe only for documentation
purposes in legacy systems. But don't take away all the fun by starting with
tests. You'll paint yourself into a corner.

This seems like 30 years of experience in a corporate environment. I would
take these recommendations with a grain of salt. I would venture to say that
whoever starts with "I have x years of experience" or "I'm an Y language
developer" is mostly bulshitting you or doesn't really know what he's talking
about, so he'll need to have a badge to shove it up your face to demonstrate
his qualities.

~~~
de_watcher
> Tests? No, definitely not on the first run. Maybe only for documentation
> purposes in legacy systems. But don't take away all the fun by starting with
> tests. You'll paint yourself into a corner.

Trying to squeeze in the tests into existing system is painful. So better do
the tests before it had become a "legacy system to document".

Tests should be written before and during writing the new system.

That's because they are the executable language for the results you want to
get from the system. The two other options are: use plain text to document
your manual actions and expected results, don't document your manual actions
and expected results.

~~~
protonimitate
>Trying to squeeze in the tests into existing system is painful. So better do
the tests before it had become a "legacy system to document".

This, but also if you code first then write tests you try to write tests based
on your interpretation of how the code you already wrote currently works, when
its better to write tests to outline and define how the code _should_ work in
the future.

>You'll paint yourself into a corner. I find it is the exact opposite. If you
write all your code first, then write tests, you will inevitably find a bug
and refactoring working code to fit a test is infinitely harder than adding
more tests or replacing irrelevant ones.

