Qafoo GmbH - passion for software quality
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Author: Kore Nordmann
:Date: Wed, 27 May 2015 10:58:03 +0200
:Revision: 14
:Copyright: All rights reserved
==============================
Developers Life is a Trade-Off
==============================
:Description:
One of the most important skills of a developer is to make trade-offs.
:Keywords:
developer, trade-off, decision making, object oriented design, skill,
challenge
:abstract:
At Qafoo, we train a lot of people on topics like object oriented software
design, automated testing and more. It happens quite often that an attendee
asks questions like "Which is the best solution for problem class $x?", "What
is the optimal tool for task $y" or "There is a new technology $z, is that the
future of web development?". Some are disappointed when I reply "It depends"
or "That does not exist", but that's the truth.
There is no silver bullet and one of the most important skills every developer
needs to hone is to assess possibilities and to find the best trade-off for the
current challenge.
To make that point clear I'm giving three examples from my personal experience,
some where it went well and some where it did not.
At Qafoo, we train a lot of people on topics like object oriented software
design, automated testing and more. It happens quite often that an attendee
asks questions like "Which is the best solution for problem class $x?", "What
is the optimal tool for task $y" or "There is a new technology $z, is that the
future of web development?". Some are disappointed when I reply "It depends"
or "That does not exist", but that's the truth.
There is no silver bullet and one of the most important skills every developer
needs to hone is to assess possibilities and to find the best trade-off for the
current challenge.
To make that point clear I'm giving three examples from my personal experience,
some where it went well and some where it did not.
The NoSQL Dilemma
-----------------
Choosing a storage system is probably the most common decision to be made in
any web project. For one project we already knew that there will be a large
amount of data, so we started RnD on recent storage solutions and NoSQL was the
big topic then. Cassandra appeared to suite our needs perfectly from its
description. So we gave it a try and prototyped against it, using a thin class
layer that hid the database detail.
It was a wise decision to create that layer whereas going with Cassandra from
the start was not a very good one. While the database met our expectations, it
crashed multiple times with loosing data in our development VMs and none of us
had a clue, why, or more importantly, how to fix it. So we revised that
decision and eventually went with MySQL and a denormalized schema, where only
important attributes became dedicated table fields and the main data structure
was serialized JSON in blob. That worked fine for several years.
What did we do? We found the solution which promised to fit our requirements
quite well. Luckily, we did not jump blindly on it, but kept it a bit away
from the system and evaluated further. Constraints like "knowledge on
maintenance" were eventually considered and so we made a trade-off between
these requirements that pushed us in contrary directions.
Overengineered State Machines
-----------------------------
You don't need to jump on such a high-level bandwagon to find examples on where
you need to make trade-offs. In one project I created a component to match our
business model against a 3rd party one to use their data in our system. While
the models were similar, a fundamental difference was how it was determined
when data was published and deleted.
That problem appeared perfect for applying a state machine to it to trigger
changes on our side when the 3rd party model changed. So I went the extra mile
by creating an abstract state machine with ``Node`` and ``Transition``
classes. A transition was triggered through an abstract ``Condition`` and a
node fired an ``Event`` which eventually triggered that change in our model. It
was beautiful code in my eyes, with really small classes (1-2 lines
executable code). To "ease" configuration and since I expected frequent
changes, I made it configurable in XML.
Some time later, a co-worker needed to check if the implemented logic still met
new requirements. Digging through that, starting from the XML configuration,
running through so many classes to get the complete picture of what it did, took
him a large amount of time. It turned out that the logic was fine, but the
amount of time spent re-occured some more times after that experience.
A year later, we needed to finally adjust the logic. But the model did not fit
the new requirement well, so that we needed to implement many additional
classes and the XML configuration became even more complex.
What went wrong? I did not evaluate all available options and pick a good
trade-off for the situation. As a minimal solution, I could have hidden the
state processing behind a class and code it straight with nested ``if``
conditions. Maybe that would have been 40 lines of code, but a good bunch of
unit tests could have covered that. Maybe a solution in between, abstracting
only the states and hard-coding the trigger and transition logic, would have
been the optimal trade-off.
.. note::
Get a Qafoo expert on-site to `train your team in automated testing`__
and support you in developing more efficient and maintainable web software.
__ /services/training.html
So, what's the moral of the story? Whenever you take a software design decision
in your project, there is a ton of possible solutions. Just picking a random,
interesting, clean, … one is most probably not the right choice.
Instead, you need to check your actual constraints and then find the best
trade-off between the possibilities. Which one that is can vary greatly.
Hack Hack Hack
--------------
There are times in a project, where the option of just hacking a problem
solution into the core of the system looks viable. Be warned explicitly: only
do that with great care and foresight. In many
environments that can lead to stacked up technical debt and
possibly result in a really hard to maintain project.
In that project we had an import workflow which performed time-costly analysis
on the imported data. It worked well as the incoming chunks of data were
small. But then a party registered which provided huge chunks of data and the
system became unresponsive. Not only that their imports were delayed, but it
affected all other involved parties, too. On the other hand, that
big-chunk-party did not even need all the analysis we performed on their data.
A clean solution would have been to create a configuration flag for the kinds
of analysis. Another one would have introduced sharding for the import process,
that was already in the backlog with low priority. Both solutions would have
required too much time, we needed to fix the actual problem immediately. So we
hacked a condition into a prominent place in the core, skipping the expensive
analysis for the big-chunk-party.
I had an eye on that code quite for a long time, being prepared to refactor it
whenever viable. But there was no need. The code
worked fine, nobody needed to touch it again and the problem was solved.
Eventually, the expensive analysis was not necessary at all anymore, so we
removed it and with it the ugly hack.
What did we actually do? We made a trade-off. That one was very much in one of
the possible directions, but because we knew of the potential problems and were
prepared to revise the decision, we could take the risk.
Bottom Line
-----------
One of the most important tasks of a developer is to make trade-offs. They
occur wherever you look in your every day life. It is a highly important step
to realize and accept this. And it is important to hone that skill. You need to
open your mind for new technology and techniques, learn and try them wherever you
can. But then you need to step back, analyze the current situation and then
find the best trade-off between all possible approaches. In addition, you need
to be able to reflect your decisions and be prepared to revise them. The plus side:
by doing so, you will surely learn something for all the upcoming trade-offs.
..
Local Variables:
mode: rst
fill-column: 79
End:
vim: et syn=rst tw=79
Trackbacks
==========
Comments
========