Published by Marco on 30. May 2014 08:28:11
Updated by Marco on 30. May 2014 08:29:45
------------------------------------------------------------------------

In the "previous article" ,
we listed a lot of questions that you should continuously ask yourself when
you're writing code. Even when you think you're not designing anything, you're
actually making decisions that will affect either other team members or future
versions of you.

In particular, we'd like to think about how we can reconcile a development
process that involves asking so many questions and taking so many facets into
consideration with YAGNI.

[Designing != Implementing]

The implication of this principle is, that if you aren't going to need
something, then there's no point in even thinking about it. While it's
absolutely commendable to adopt a YAGNI attitude, not building something doesn't
mean not thinking about it and identifying potential pitfalls.

A feature or design concept can be discussed within a time-box. Allocate a
fixed, limited amount of time to determine whether the feature or design concept
needs to be incorporated, whether it would be nice to incorporate it or possibly
to jettison it if it's too much work and isn't really necessary.

The overwhelming majority of time wasted on a feature is in the implementation,
debugging, testing, documentation and maintenance of it, not in the design.
Granted, a long design phase can be a time-sink -- especially a "perfect is the
enemy of the good" style of design where you're completely blocked from even
starting work. With practice, however, you'll learn how to think about a feature
or design concept (e.g. extensibility) without letting it ruin your schedule.

If you don't try to anticipate future needs at all while designing your API, you
may end up preventing that API from being extended in directions that are both
logical and could easily have been anticipated. If the API is not extensible,
then it will not be used and may have to be rewritten in the future, losing more
time at that point rather than up front. This is, however, only a consideration
you must make. It's perfectly acceptable to decide that you currently don't care
at all and that a feature will have to be rewritten at some point in the future.


You can't do this kind of cost-benefit analysis and risk-management if you
haven't taken time to identify the costs, benefits or risks.

[Document your process]

At Encodo, we encourage the person who's already spent time thinking about this
problem to simply document the drawbacks and concessions and possible ideas in
an issue-tracker entry that is linked to the current implementation. This allows
future users, maintainers or extenders of the API to be aware of the thought
process that underlies a feature. It can also help to avoid misunderstandings
about what the intended audience and coverage of an API are. 

The idea is to eliminate assumptions. A lot of time can be wasted when
maintenance developers make incorrect assumptions about the intent of code.

If you don't have time to do any of this, then you can write a quick note in a
task list that you need to more fully document your thoughts on the code you're
writing. And you should try to do that soon, while the ideas are still
relatively fresh in your mind. If you don't have time to think about what you're
doing even to that degree, then you're doing something wrong and need to get
organized better.

That is, you if you can't think about the code you're writing and don't have
time to document your process, even minimally, then you shouldn't be writing
that code. Either that, or you implicitly accept that others will have to clean
up your mess. And "others" includes future versions of you. (E.g. the you who,
six months from now, is muttering, "who wrote this crap?!?")

[Be Honest about Hacking]

As an example, we can consider how we go from a specific feature in the context
of a project to thinking about where the functionality could fit in to a suite
of products -- that may or may not yet exist. And remember, we're only thinking
about these things. And we're thinking about them for a limited time -- a
time-box. You don't want to prevent your project from moving forward, but you
also don't want to advance at all costs.

Advancing in an unstructured way is called hacking and, while it can lead to a
short-term win, it almost always leads to short-to-medium term deficits. You can
still write code that is hacked and looks hacked, if that is the highest current
priority, but you're not allowed to forget that you did so. You must officially
designate what you're doing as a hot-zone of hacking so that the Hazmat team can
clean it up later, if needed.

A working prototype that is hacked together just so it works for the next
demonstration is great as long as you don't think that you can take it into
production without doing the design and documentation work that you initially
skipped.

If you fail to document the deficits that prevent you from taking a prototype to
production, then how will you address those deficits? It will cost you much more
time and pain to determine the deficits after the fact. Not only that, but
unless you do a very good job, it is your users that will most likely be finding
deficits -- in the form of bugs.

If your product is just a hacked mess of spaghetti code with no rhyme or reason,
another developer will be faster and produce more reliable code by just starting
over. Trying to determine the flaws, drawbacks and hacks through intuition and
reverse-engineering is slower and more error-prone than just starting with a
clean slate. Developers on such a project will not be able to save time -- and
money -- by building on what you've already made.

[A note on error-handling]

Not to be forgotten is a structured approach to error-handling. The more
"hacked" the code, the more stringent the error-checking should be. If you
haven't had time yet to write or test code sufficiently, then that code
shouldn't be making broad decisions about what it thinks are acceptable errors.

Fail early, fail often. Don't try to make a hacked mess of code bullet-proof by
catching all errors in an undocumented manner. Doing so is deceptive to testers
of the product as well as other developers.

If you're building a demo, make sure the happy path works and stick to it during
the demo. If you do have to break this rule, add the hacks to a demo-specific
branch of the code that will be discarded later.

[Working with a documented project]

If, however, the developer can look at your code and sees accompanying notes
(either in an issue tracker, as TODOs in the code or some other form of
documentation), that developer knows where to start fixing the code to bring it
to production quality.

For example, it's acceptable to configure an application in code as long as you
do it in a central place and you document that the intent is to move the
configuration to an external source when there's time. If a future developer
finds code for support for multiple database connections and tests that are set
to ignore with a note/issue that says "extend to support multiple databases",
that future developer can decide whether to actually implement the feature or
whether to just discard it because it has been deprecated as a requirement.

Without documentation or structure or an indication which parts of the code were
thought-through and which are considered to be hacked, subsequent developers are
forced to make assumptions that may not be accurate. They will either assume
that hacked code is OK or that battle-tested code is garbage. If you don't
inform other developers of your intent when your're writing the code -- best
done with documentation, tests and/or a cleanly designed API -- then it might be
discarded or ignored, wasting even more time and money.

If you're on a really tight time-budget and don't have time to document your
process correctly, then write a quick note that you think the design is OK or
the code is OK, but tell your future self or other developers what they're
looking at. It will only take you a few minutes and you'll be glad you did --
and so will they.