
Three ways to solve hard programming problems - ingve
http://jvns.ca/blog/2016/05/29/three-ways-to-solve-hard-programming-problems/
======
falcolas
> Realize that there is awesome existing software that you can repurpose

Every time I see this, I find that the problem of leaky abstractions result in
a 3 phase solution. It's awesome in the beginning, everything "just [mostly]
works". Then a few months in, you start to feel like a young dutch male with
his finger stuck in the earth, holding back the ocean. In the end, you throw
it out, having "stolen" the important bits for yourself.

Sometimes, you find yourself stuck at one of the first two phases. I wish it
was more frequent.

~~~
golergka
The longer you have to maintain the software, the more of an improbable
scenario (in the eyes of past view who made a decision on maintainability) you
become. I'm not saying that all software should be implemented in the most
quick and hacky way; rather, that you need to strike a balance. And in a lot
of situations, you don't actually need to build a well-maintainable,
modifiable and extensible solution.

It just feels better to build such a solution as an engineer. Stories about
these solutions are much more interesting than "I used a ready box and saved
the company two weeks of my time so I can spend it on another feature of a
prototype". And usually, the better technical level of a software developer,
the more he prefers the long, right, rigid way to do Things That Last.

But from entirely business perspective, this is actually good advice in a
surprisingly big share of the situations.

~~~
lostcolony
The problem is is that outside of startups, anything that works even halfway
is going to have a long shelf life. I have seen exactly zero instances where a
software solution was end of lifed earlier or at the time it was predicted to
be in the enterprise (if an EOL was even defined beyond the implicit one
developers set of "if it breaks after I'm gone it ain't my problem"); every
single instance either had not hit its predicted end of life time yet, or was
well, well passed it. Even stuff that -doesn't work at all- will be funded for
refactoring rather than a rewrite.

Given that, it makes sense to, in that environment, default to "let's do this
thing correctly". But that's not actually long, right, and rigid. It's often
shorter than one might think, and far more flexible, as it usually means
"let's do the minimum amount of features that we need, but what ones we do,
let's make sure we do well, working with the user to define them, and get
feedback/iterate, writing lots of test, documentation, etc along the way". The
problem is that businesses tend to prioritize exactly the opposite; give us
features, now now now, that have only been defined at a very high level and
not actually thought about by anyone, and any time not spent on implementing
features is wasted. And with that mentality, using an existing solution seems
to make sense. It's only afterwards that you realize that solution brings a
lot of unnecessary complexity, doesn't give you the level of control you want,
didn't instill the necessary domain knowledge in the developers for proper
maintenance and modifications etc, possibly didn't even solve the problem(s),
but by then it's too late, you're just told to make it work.

~~~
kbenson
I think an important aspect that is often overlooked is how likely are the
original developer(s), or people that really understand the purpose, going to
be around later if it needs more work or a rewrite.

For example, I often take the approach that my first implementation, which
likely will end up in production, is used for learning the interface, service
or business need. It satisfies the problem as it currently exists and was
defined, and generally has a few hacky parts as the needs were explained in
more detail or changed halfway or more through the project. The next time
anything more than a bug fix or trivial feature is required, I'll rewrite the
most problematic behavior in a more extensible and cleaner way, since I now
have a much better understanding of the problem. This is generally quick, as I
can re-purpose portions of the original that are perfectly usable, and I have
a reference implementation (the original) to compare against to find any bugs
in the new version.

I can rely on this because the chances that I will be around at this company
in the future are extremely high. This would not be a good approach for a
company that sees domain expertise leave, or move to different departments
that aren't easily approached for information. I've been on projects where you
come in to rewrite on someone else's old code because nobody really
understands it well, with little or no information besides the code itself and
included comments as to the original design decisions. It's easy to identify
the portions of the code that look like they were stupid decisions, and you
would _definitely_ never do that. You start writing code, and soon you run
into unexpected conditions that require you to make the same or equivalent
hacky choices, and you understand a little of the original developer's prior
choices. You'll end up with the code rewritten, and possibly a little better
and cleaner, but if someone comes along in a few years, they'll have the same
problems you did. These are the situations you want to avoid by spending more
time up front to make the solution better in the beginning, or at the _bare_
minimum, documenting in the code the hacky abstractions you've been forced to
make.

------
match
Another often practical approach is to change the situation in some way that
allows you to solve an easier problem instead. This isn't always possible but
can be more often than you might think.

~~~
michaelwww
I go further and ask myself if the problem needs to be solved at all. A lot of
premature optimization falls into this category.

------
brudgers
The canonical example of employing existing tools might be McIlroy's shell
script used as a critique of Knuth's solution to word counting [search for
"knuth" here:
[https://www.princeton.edu/~hos/mike/transcripts/mcilroy.htm](https://www.princeton.edu/~hos/mike/transcripts/mcilroy.htm)].

In fairness, I think a more intellectually honest critique would have been to
write the script using literate programming. Sure it might have required
McIlroy to write a tool for literate shell programming -- the will to do so
being the magic trick separating Knuth from mortals. The world might have been
better for it.

------
Myrmornis
Sadly what is needed is usually just a clear understanding of the
requirements, careful study of existing code and documentation, new code,
writing new automated tests, and careful manual testing.

------
nthcolumn
Beg, borrow or steal.

