
Attitudes That Lead to Maintainable Code - ingve
https://spin.atomicobject.com/2017/03/07/attitudes-maintainable-code/
======
cousin_it
I think you can write any program using only advanced concepts from the
problem domain, not advanced concepts from programming itself. For example,
I'll happily implement a difficult numerical algorithm for inverting a matrix,
but I won't write a monad framework in C++ for that. IMO there are almost no
projects that call for programming cleverness (as opposed to problem domain
cleverness). You can get very far with boring imperative code that any
beginner can read, with powerful algorithms introduced only locally and
documented clearly.

~~~
organsnyder
I rarely solve difficult programming challenges. I solve difficult business
challenges all the time.

~~~
phantarch
I'll echo that sentiment, I experience it daily. Very rarely is a business
itself abstract and complicated enough to merit an abstract and complicated
technical solution. Meatspace is still pretty simple.

~~~
chimprich
There's often a non-complicated solution to a difficult problem but spotting
it takes some skill and experience and sometimes takes longer to implement
than a more obvious complicated solution.

~~~
AstralStorm
And sometimes complicated problems can really be solved only by a complicated
solution, but can be half-assed in a simple way. You pay for the partial
solution much later.

It is good to avoid future discounting in this way.

------
blowski
The three rules that I most frequently quote:

1\. Code to an interface, not an implementation. Think about the essence of
the class, without which, it would be a different class. Then build an API for
that essence, then implement the API.

2\. Program into a language, not in a language. Work out first what you want
to achieve, then figure the best way of achieving that in the language.

3\. Tell, don't ask. Don't expect client systems to know how your system
works. When the state of your domain has changed, tell other systems, don't
wait for them to ask.

The "Factorio" game is one of the best explanations of good programming I've
ever come across. Build components that do one thing well, then build
components that use those components, and keep growing the system in that way.
When you want to keep the relationship between two components more flexible,
push values from the first component onto a 'conveyor belt' and have the
second component pick up values off that belt. Service oriented architecture
in game form.

~~~
buserror
I don't agree fully to that (1). I _used_ to do it, because it sounds like a
great idea... However, the implementation of _your use case_ is often too
subtle to get the API right to start with, and you end up having to play
around and 'adjust' your API. ie, maintaining it even tho it doesn't even
exists yet!

My method is now to implement a 'dirty' integration, without any care of
structure or cleanliness, then when it's in place and working _only then_ do I
sit down and look at the API I would have liked to have, and implement _this_
by mostly refactoring what is already working.

With that method, you end up with something that is clean, and more
importantly does _exactly_ what is needed an no more. There is no bits 'that
could be useful later' and are never implemented, or used, or tested, and the
model fits the job.

~~~
sodapopcan
Yeah, this really reminds me of Sandi Metz's "Duplication is less expensive
than the wrong abstraction." I know we aren't talking duplication specifically
here, but I've eagerly built the wrong abstraction several times and it
suuuuucks.

These days I'm all for the "dirty integration" first.

~~~
AstralStorm
If you have proper functional tests, you can replace wrong abstractions at a
cost.

The real issue is code abstracted bottom-up instead of proper design. Such
abstractions end up being special cases and become annoying and worthless if
requirements (incl. internal ones) change.

While sometimes you can design yourself into a bad end (typically by not
taking a really critical requirement into consideration), this is not what
often happens.

~~~
sodapopcan
> The real issue is code abstracted bottom-up instead of proper design.

Exactly. We're (basically) saying the same thing here. I kept my comment
short-and-sweet as not to get into the nitty-gritty of everyone's different
work situations/requirements/stances on different testing practices/building
an app vs a library or service/programming paradigm/etc.

To expand ever so slightly: "These days I'm all about 'dirty integration'
first to help inform my ultimate design."

------
taeric
The number one attitude that leads to maintainable code for me, is to not
worry about problems I don't actually have.

That is, solve the problem I am trying to solve. Do not go out of the way to
solve other problems. They will have their time, or they were not important.

Obviously, it took some time to get so that I could write code that wasn't a
disaster on its own. I would argue I haven't gotten past that point, actually.
Which is all the more reason that the less I touch to solve a problem, the
better.

~~~
gentleteblor
I'm not sure how I feel about this.

What has worked for me is embracing change.

Requirements will change, third party integrations will change, front end
frameworks, use cases, service providers etc.

Everything will change so write (really build) code that tolerates change
(IOC, interfaces, etc. Any form of loose coupling you can get away with
cleanly).

There are of course situations when the one line fix is the right one. But in
general, this outlook on short term vs long term changes (knowing when the
concrete should become the abstract) is one of the differences between being a
programmer and a software engineer (or being a senior and a junior).

~~~
mrec
> _Everything will change so write (really build) code that tolerates change
> (IOC, interfaces, etc._

Everything _might_ change. Not everything _will_ change.

I think the problem I have with your principle is that many of the techniques
to accommodate change (the "IOC, interfaces, etc" you mention) can have a real
and immediate cost when it comes to understanding a codebase, and that's __the
__fundamental sin when it comes to maintainability.

Abstraction and indirection have value, obviously, but on balance I'm with GP;
don't add 'em until you actually need 'em. In particular, don't add an
interface and a factory and whatnot until you actually have more than one
implementation.

This may depend on your languages and dev environment; something like IDEA
where large-scale but mechanically trivial refactorings can be accomplished
quickly and safely are more suited to KISS than e.g. dynamic languages where
tooling is much much weaker.

~~~
gentleteblor
> Everything might change. Not everything will change.

True. Not everything will change. But you don't know which ones will and which
ones won't. So be ready.

> I think the problem I have with your principle is that many of the
> techniques to accommodate change (the "IOC, interfaces, etc" you mention)
> can have a real and immediate cost when it comes to understanding a
> codebase, and that's the fundamental sin when it comes to maintainability.

Not true. The benefits of IOC and loose coupling are well established and I
won't even bother relisting them here. I'll only say that those benefits
usually outweigh the minor indirection you get as a result.

The question then becomes, do you loose couple from the beginning? or tightly
couple everywhere and loosen as you go. I tend towards doing it from the
start, for consistency sake (help those poor junior engineers out), and for
all the other benefits (which again, i don't want to list, but i'll add the
one biggie - unit and integration testing).

I'll also note that IOC/loose coupling is rarely the cause of the over
abstraction you fear. I've seen it way more in domain models or APIs.

There are many fads, new fangled doo-dads, thingamajigs in our software world.
New frameworks, seismic shifts in architecture, herd mentality etc. IOC/loose
coupling isn't one of them. It's good, old engineering practice.

~~~
taeric
You underline what is essentially my biggest gripe with IOC. There seemed to
have been an explosion of everyone wanting you to use a new fancy framework to
make it happen.

For the most part, constructor logic that simply sets dependent fields (and,
importantly, does not do anything) along with not requiring fancy
initialization shakeups goes a long way to easily testable things.

That said, people that go out of their way to make a fixture for each
individual method can be just as annoying as the folks that only do end to end
testing.

Basically, life is easy to argue at the strawman level. Breaks down quickly
when there are "boots on the ground." (Or whatever other saying works.)

~~~
gentleteblor
I'm with you there man (or lady)...the pace of "new and shiny" is exhausting.

One of the things I like about our environment is that we're always disrupting
ourselves (maybe a little too much). So while it's true that there are many
heavy weight DI tools (Castle.Windsor for ex), there are also lighter-weight
ones (Ninject and co). At the end of the day, we each pick our heavy we want
our tools to be (we can always implement IOC without a DI tool if you want to
keep things bare boned).

> Basically, life is easy to argue at the strawman level. Breaks down quickly
> when there are "boots on the ground." (Or whatever other saying works.)

It's an interesting discussion for sure. And some of it is philosophical. When
the "rubber hits the road" (or whatever saying works) and i'm stuck in the
office at 2am trying refactor a ball of mud, I wish those who came before me
had thought of this stuff.

Engineering. Not Programming.

~~~
AstralStorm
I find it extra funny when "new and shiny" is actually a 30 years old idea in
a new spin. (Reactive programming haha.)

------
edw519
Nice post. Short, sweet, and presented to avoid endless debates over
particulars. (I know, I have plenty of my own.) I especially like:

 _I’ve found it’s often easier to keep a few broader ideas in mind._

Funny, OP doesn't mention the broadest idea of all: ego.

I've worked with hundreds of programmers and have sensed an inverse
correlation between experience and openness to suggestions about maintainable
code. I believe that the main reason is not that the more experienced
developers are convinced that their methods are that much better (after all,
there are often many acceptable ways), but that their ego won't let them "lose
a debate".

When I return peer reviewed code to a young developer with suggestions (and
reasoning!) about how to make it more maintainable, I often get a big thank
you. The same feedback often gets a debate from a more experienced developer.

On the other hand, when someone gives me feedback and suggestions, my first
reaction is to engage them and explain why they're wrong and I'm right. But as
soon as I set my ego aside and think of the code instead of myself, I open
myself up to learning. This is vital because what I don't know is always much,
much more than what I do.

~~~
js8
> The same feedback often gets a debate from a more experienced developer

Maybe that's because they have the experience they are able to debate it!

I think "maintainable" will always be debatable, because we are not using
scientific methods to determine what is more maintainable. So we are all
subjects to subtle biases like confirmation bias.

It would be great if we could use scientific method to determine how to write
more maintainable code, but you would need a good definition of "maintainable"
first, and that's hard.

Personally, I don't see much, if any, hard evidence that today's code (in 30
years to the future) will be more maintainable than the code written 30 years
ago. I work on 30+ years old legacy codebase, which is often crazy complicated
and effectively not-refactorable. But you can still fix bugs, you can add
functionality. It's expensive to do, but not impossible. Would be the
accumulated cost lower if people spent more time refactoring it, like today
sometimes happens? Not clear at all.

------
grabcocque
The most maintainable line of code is the one which you don't write. Remember
the cardinal virtues:

Laziness Impatience Hubris

Back when I was a Java dev, judiciously following the SOLID principles was a
vital part of keeping code maintainable. They're still as true today as they
ever were.

* a class should have only a single responsibility

* software entities should be open for extension, but closed for modification

* objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

* many client-specific interfaces are better than one general-purpose interface.

* depend upon abstractions, not concretions.

~~~
FreezerburnV
Can you expand a little about how each of these parts of SOLID have influenced
how you have written code? I feel like I've heard people say conflicting
things to some degree on at least some of these (e.g.: single responsibility)
and my own experience feels somewhat like following some of them to their
natural conclusion can end up with code that is more complex and harder to
maintain. Again, for example, the S seems to imply needing a larger number of
classes that would have to be searched through to find a specific
implementation or bug, and D feels like it would shove the code that actually
does something down a layer and be harder to find. That could be my
inexperience talking or just not knowing how to employ the ideas properly,
though.

I guess a decent amount of what I'm feeling comes from my current job, which
breaks a decent number of those rules. (S, O, I at least, possibly D but I
don't think so) But despite that, code generally seems to be pretty easy to
read through, so I'm a bit conflicted.

~~~
AstralStorm
I find SOLID to be less reliable than applying ages old principles of
contract-oriented programming. S is matter of taste, you can define it to be
"show the whole GUI" or "show a text label in given position".

Depending upon abstractions directly is much less reliable than depending upon
contract (which is usually not explicit in the abstraction) and preferably
also verifying it as both preconditions and postconditions.

The other letters are important, though software should be _designed_ for easy
modification instead of being closed to it. Otherwise you end up with
abstraction forests and deprecated code.

Many interfaces is typically a smell, as the developer now has to juggle
multiple balls in his head. There is good medium ground out there - making the
interface maximize usefulness while not sacrificing too much of Liskov
substitution capability is key.

------
quadcore
"Attitudes That Lead to Maintainable Code"

Well that assumes the code is the problem and not the programmer. The code
might be perfectly maintainable but the programmer lack skills to do just
that.

I've only seen very rarely really-hard-to-maintain code, and there were of two
sorts:

1) over-engineered pseudo-senior object-oriented spaghetti plate -- generally
resulting from methods that supposedly make the code more maintainable.

2) logically-wrong over logically-wrong spaghetti plate -- the kind of code
that programmers hate to admit they do: simply logically-wrong code.

On the other hand, I've seen plenty of beginners making critics about a code
that is supposely hard-to-maintain whereas the problem was them because:

1) they don't know how to read and learn a code base. They typically start
working on a projets and they don't read the codebase. They jump on a task.
They actually never read the codebase, but only piece of it.

2) they think the code is "obfuscated" whereas it is, well, just not. The
programmer is just a beginner that needs to learn how to read a boolen
expression for example (typically: var ok = cond1 && !cond2 || cond3 -- _omg
the code is obfuscated!_ ).

------
daxfohl
I'd argue that sometimes you _do_ want to hack things. Sometimes you go way
out of your way to avoid that one special case for that one odd requirement,
abstracting things out several layers to present a unified framework that
encompasses that one oddball. Then the next day you get another oddball thrown
at you that completely dorks it up. After a while you don't even remember why
you had all the layers. At the end of the day, coding explicitly to the
special cases and calling them out as special cases may make for more
intuitive code.

(Also see "The Wrong Abstraction":
[https://www.sandimetz.com/blog/2016/1/20/the-wrong-
abstracti...](https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction))

~~~
falcolas
For every rule, there is an exception which helps to prove the rule. Yes,
hacking is sometimes necessary and is frequently a more efficient use of your
time in the short term, but it should always be asked: "will I understand this
hack next year?"

Tomorrow's oddball might not happen, after all.

~~~
hasbot
That's where I find a VCS _really_ helpful. If I'm wondering about a
particular oddball section of code, I can go back and look at the context in
which it was introduced. This is very handy especially for code that was
written by multiple people over time.

------
spunker540
When I first started working after college I had a lot to learn. For some of
the first bug fixes or small feature enhancements I had, I remember fixing it
with a one line change, and consequently being so frustrated with the senior
code reviewer who would recommend I do some major refactoring instead.

I now see that it's not about doing things minimally, but doing things as
correctly and clearly as possible. And one should never be afraid to refactor!

(Obviously there are cases where single line changes are the correct solution;
but back then my mindset was "how can I do this with as little change as
possible" and it should have been "as clearly as possible")

~~~
amelius
It makes me wonder why programming languages (and/or their development
environments) are not designed for refactoring.

Refactoring should be as simple as possible.

~~~
caseymarquis
In what way are they not designed as such? The biggest issue I can think of
regarding major refactoring is coordinating across the team and ensuring
you're collectively at a good point to start changing things. That's not
really a language/tooling issue.

~~~
fstephany
For everyday operations and refactoring, I find the tools to be the limiting
factor.

I've just finished a contract where we had to deal with legacy code on iOS and
Android (yes, it exists. Let two junior programmers not very keen to learn
from the outside during 5 years working on the app and see with what you end
up).

Refactoring the Android code to a healthy state was greatly facilitated by
Android Studio and the whole IntelliJ suite. We were not afraid to move things
around and could always see the light at the end of the tunnel.

On the other side, XCode has been a pain in the ass for the whole project. We
had ObjC and Swift coode. Good luck with that. Even the pure Swift was hard to
refactor. SourceKit is great and should help to get a top notch IDE that can
resonate about source code but somehow Apple does not seem to care about the
tools.

Sorry, I get passionate when talking about refactoring and tools.

Another example is how the Pharo Project
([http://pharo.org/](http://pharo.org/)) is refactoring the Squeak source
([http://squeak.org/](http://squeak.org/)) code/architecture. They developed
custom tools for this purpose and their code cleaning is quite impressive when
you know where they are coming from.

------
js8
There is one attitude that's missing from the list: Change jobs frequently,
and avoid old legacy code. Then you will always write the new code and it will
be, by definition, maintainable (of course, who in their right mind would
intentionally write an unmaintainable code?).

~~~
usrusr
So that you will never see wether the code is actually maintainable or not?

You have described a strategy to avoid having to deal with bad
maintainability, not a strategy to avoid creating bad maintainability.

~~~
js8
Quite clearly, I was being sarcastic. In fact, on three different points:

1\. I see "unmaintainable" as mostly a factor of time (or age of the
codebase), of changing requirements and people working on the code. So giving
advice on how to write the code to be maintainable isn't gonna help much. If
people turnover quickly, they won't bother, if they turnover slower, they will
do their best to keep it maintainable and yet fail.

2\. People who follow (intentionally or not) the advice I gave only ever see
maintainable code, so from their perspective, it leads to (having to deal
with) maintainable code.

3\. (= 1. + 2.) Sadly, the people who are the most displeased with the legacy
code not being maintainable are the ones who tend to leave quickly (or write
something on their own), paradoxically, compounding to the problem of the
unmaintainable code in the first place!

I don't think there is a good solution to the problem. Maintaining old code is
hard work.

~~~
usrusr
Ok, I see it now, but the liberal conflation of "maintainable" and "code that
is easy to work on" that is repeated in 2. still hurts my eyes: they don't see
maintainable code, they see code that is easy to work on - not because it is
maintainable, but because of its position in the code lifecycle. The confusion
between these two is exactly your point, sure, but I don't know how to say it
better, I think your wording is just too close to subscribing to it yourself.
They _think_ they see maintainable code. I know I do, while writing what I
will later call a hopeless mess.

(Written by someone who has been doing brownfield projects, often bordering on
code archeology, at the same employer for far too many years)

~~~
js8
Not really sure what your objection is; what exactly difference you see
between "maintainable" and "easy to work on"?

Could it be that a good definition of maintainable is simply "easy to
understand by others"? Then, of course, code earlier in the life cycle tends
to be easier to understand.

Then there could be a difference between "easy to work with" (by the original
author(s)) and "easy to understand by others". But assuming original authors
were good programmers, I am not sure how they could cause this difference to
occur.

------
partycoder
Some positive sounding concepts that are used to wreck code and software
engineering at a large:

\- "Get it done attitude": Declare something as done while it's still a
prototype.

\- "Fail early": start with prototypes, end with prototypes. ship a prototype
to production.

\- "Knowing the software business": know how to trick non-technical people
into buying overpriced prototypes made of spaghetti.

\- "Product driven", "Feature driven", "Customer driven": implement only what
can be sold (features), disregard everything else. e.g: security, scalability,
etc.

It is becoming a business model to sell prototypes as finished products.

~~~
sidlls
I'm experiencing this in my current job, and it's a bit demoralizing. It seems
"agile" has been internalized to some extent to mean "start writing code on
day 1 or else we're basically doing waterfall which sucks," too.

~~~
partycoder
Agile is often cherry-picked and reduced to only the parts that people like.
Burn-down charts are ubiquitous but sprint retrospectives are not.

Then some other parts are conveniently misinterpreted. People misunderstand
the role of the scrum master and interpret it as the development team
leader... confuse points with hours, or try to measure team performance using
velocity... which is the same as measuring a truck driver performance by how
much gas they're using rather than concrete results.

Everyone claims to be doing scrum correctly, just like everyone claims to be
doing git-flow correctly. It's not only cool to be agile and use scrum, it's
more like a dogmatic cult where scrum is the ultimate truth that solves every
problem in every case and cannot be criticized.

------
codr4life
What I'm reading is mostly management directives for getting a bunch of code
monkeys to produce code in the safest, most generic and repeatable way
possible; by enforcing the lowest common denominator. Given a sound team
culture, not using the full power of tools and limiting output to dumbed down
templates are anti-patterns; it's not like the job isn't difficult enough the
way it is.

------
hashkb
Another tautology filled article about how to write good code you just need to
write good code.

~~~
coldtea
The article breaks down some things that you need to do to write good code.
Proper naming, write self-documenting code, no fancy stuff, have the correct
indentation and other formatting, avoid special cases, etc.

Which is more than we can say for this dismissal.

------
_pmf_
Somehow, a lot of people seem to write maintainable code, but everybody always
ends up having to maintain unmaintainable code.

A strange thing, this.

------
rkangel
> Comments should be reserved for the rare bits of code that are influenced by
> factors beyond the code itself (for example, a customer requirement or a bit
> of historical context)

This statement may be true in language domains the author is used to, but is
certainly not true across the full spectrum. Also - there are two sorts of
comments: 'what' comments, and 'why' comments.

'What' comments tell you what the code is doing. A lot of the time, in a lot
of languages, if you code clearly these are needed only rarely, if at all.
They are telling you what the code is doing, mostly as a convenience - they
can always be replaced by spending time reading the code to see what it's
doing. There are a number of situations where they are useful though, mostly
obviously in languages like C or even Assembly where code often cannot be made
easily readable. The 'what' comments allow you to quickly parse code to build
up understanding of what's going on. Good assembly has a very high comment to
code ratio - you are mostly reading the comments rather than the code to grok
it. Occasional 'what' comments on a bit of C pointer arithmetic allow you to
understand the authors intention and check against that 'specification'. I
agree that in more expressive languages, these sort of comments become rarer
and rarer.

'Why' comments are irreplacable in every language, and are what the author
seems to be referring to in the above quote. "I have chosen to do this like
this here because...". Because otherwise X unforseen bug, because otherwise Y
performance issue, because Z client requirement. However expressive your code,
that extra context is never going to be communicated by the code itself.

~~~
charles-salvia
I agree with the comment about assembly of course, but ironically I find
myself spending more time writing comments when programming in high level
languages like Python, rather than in C. The reason most of the time seems to
be that many high level scripting languages (like Python) are dynamically
typed languages. So things like function definitions contain no information
(other than the variable name) that explains what the inputs/arguments might
actually be. I often find myself having to write comments to make it clear
what the parameters are supposed to be so someone reading the code instantly
understands what needs to be passed to the function, whereas in a language
like C or C++ that has static typing, the type would make it obvious.

~~~
chriswarbo
> I often find myself having to write comments to make it clear what the
> parameters are supposed to be so someone reading the code instantly
> understands what needs to be passed to the function, whereas in a language
> like C or C++ that has static typing, the type would make it obvious.

I'd encourage turning these sorts of 'comments' into Javadoc-style
'annotations'; they're still comments, so language implementation will ignore
them, but that small amount of structure makes it possible for separate tools
to perform sanity checks (i.e. obvious combinations of incompatibly-annotated
types), or even to show annotations at call sites in an IDE (e.g. when
hovering over a name).

------
mooneater
The biggest factors to me: use appropriate, future proof (insofar as possible)
reliable tools and frameworks. Favour popular established libraries over
custom code. This can dramatically reduce surface area you need to maintain,
and simplify the code that remains. Simple but often neglected.

------
keithnz
If we are talking attitudes, then what's really needed is to care about code
you write. I don't think you want terribly many hard and fast rules, but a
considered approach to everything. Cleverness is great when appropriate.
Things shouldn't be used just because you want to use them, things get used
because they enhance the engineering. I think the greatest danger is probably
having too many rigid ideas about coding.

------
randyrand
Another big one. For Object oriented code. Composition over inheritance. It's
hard to overstate how important this is. Many people know this but still don't
do it enough.

Don't make large classes that implement many features. Split those
features/responsibilities into smaller classes and then have a class that
calls to the smaller ones.

A good way to think about this is _your classes should have as few member
variables as possible._

------
mrweasel
I really agree with the "Avoid special cases", or the Zen of Python version:
"Special cases aren't special enough to break the rules."

For large systems it's absolutely critical to be careful around special case,
at best avoid them. The hard part of dealing with special cases is to explain
to business people that their "It's just ONE special case, how bad can it be"
will snowball if allowed.

------
golergka
It seems down. Google cache link:
[https://webcache.googleusercontent.com/search?q=cache:sGxWmx...](https://webcache.googleusercontent.com/search?q=cache:sGxWmxEUEfwJ:https://spin.atomicobject.com/2017/03/07/attitudes-
maintainable-code/+&cd=1&hl=en&ct=clnk&gl=il)

------
coldcode
I've always had the attitude that its easy to not put bugs in in the first
place than to have to find and fix them later. Whatever you do that makes the
code simpler and clearer up front, which might take longer, is always saved in
the end when there are fewer things to fix. I started thinking this way 30
years ago and it's always done me well since.

------
chousuke
I think this applies to more than just coding.

Being a sysadmin, code that I write tends to be in the form of puppet
manifests, and these things do apply. There's one more rule of thumb I use to
determine how I should approach building systems. Basically, I think that
shortcuts are not (usually) worth the trouble.

What I mean by this is that usually the least-effort, fastest (or "obvious")
solution to a particular problem is not worth the time saved, when compared to
just spending the time figuring out how to do things "properly".

There is a continuous cost-benefit analysis where you keep asking yourself
whether you will save you enough time taking the shortcut that potentially
having to re-do it later (or fix potential issues caused by it) will still end
up less "expensive".

When you do choose to take things slow, though, you usually end up having a
better understanding of the problem as well as the solution, which naturally
leads to a better result.

------
collyw
The documentation for the application I maintain is scattered with similar
buzzwords.

Unfortunately the database design is crap leading to way more code than is
necessary. It also includes important information that has been pickled and
stored in a field, so its a nightmare to debug. It's done in Django but he has
used a "fat controllers thin models philosophy" \- the opposite of Django best
practices, so it doesn't take advantage of many of Django's features such as
lazy loading and doesn't run quickly as a result.

------
JCDenton2052
YAGNI. Not being an abstraction freak. Not implementing hundreds of features
in the hope that they may be useful one day.

I have been going through the code of a well-known document database... I am
horrified to discover over 400 classes in their abstractions project,
including some very clever stuff, which I then try to find examples of in the
rest of the code, only to come up with nothing.

------
galdosdi
The HN comments on this are a lot more interesting than the article itself.
Just a couple of platitudes we've all heard and repeated and lived by. "You
can write more maintainable code if you think about making it easier to
maintain!" OK, duh.

------
maxxxxx
Be consistent. Pick one style and use it everywhere. It's nice if you can
predict how something will be implemented from previous experience. And don't
be fancy.

~~~
AstralStorm
Style is overrated. Changing attitudes and requirements force different
styles. It is not an accident of nature that generally the nicest languages to
use are multi-paradigm, therefore allow multiple styles.

Previous experience can be very misleading. It is safest to come at the code
and design as a blank slate. Prediction (also known as educated guesses) is
worse than actual understanding, especially deep understanding.

Sometimes being fancy saves a lot of effort down the road. It is being fancy
for its own sake or for purely imagined reasons that is dangerous.

------
yawz
We write code to be read by humans, and not by computers.

------
MichaelMoser123
Avoid special cases. Thats a good idea, unfortunately it's not quite possible
if you have to adjust code to changing requirements over a long time...

~~~
AstralStorm
It should be rephrased as: design so that many cases can be handled, thus
reducing number of special cases.

They cannot be avoided, but their impact can be reduced. If you end up
drowning in them, it means the design is broken and needs to be rethought.

------
weaksauce
I like the alternate quote from the article:

There are only two hard things in computer science: cache invalidation, naming
things, and off by one errors.

------
yxhuvud
How about attitudes that lead to performance code? That page doesn't load
properly.

~~~
greenhatman
It loaded very fast for me, and I have crappy internet.

