
How to Write Unmaintainable Code (2003) - guavaNinja
https://github.com/Droogans/unmaintainable-code/blob/master/README.md
======
lmilcin
All that is really amateurish way to write unmaintainable code. I specialize
in dealing with legacy apps (diagnosing, refactoring, figuring out what to do
that would salvage them). Let me share some tips from my experience that will
really help you step up your game (real projects I have joined):

1\. Don't write it yourself. Your expertise is too precious for the company
and you work in a high cost location which automatically makes you too
expensive to actually code. Outsource it to some other team in a low cost
location. Hire entire team for the purpose of the project, ensure they know
most of them will be let go before the project end and ensure they have no
vested interest in the success of the project. Provide as little actual
guidance on how the problem is to be solved, only very general requirements.
Let the team self-organize. Don't supervise (your time too precious,
remember?) and only ever involve in discussion about high-level architectural
concepts. Expect the application to be delivered to PROD on time regardless of
the state and the missing features to be substituted with workarounds.

2\. Not happy with quality of the code? Start a rewrite without a good idea on
why exactly you are doing the rewrite, what you want to achieve or how you are
going to do it. Decide to slowly migrate functionality from the old service to
the new one using strangler pattern. Don't get buy in from your stakeholders.
Annoy them with requests for more staff while delaying development of critical
features and creating more and more incidents. Eventually put the migration on
hold having multiple services with unclear boundaries, copies of
functionality, nonexistent documentation and new development team because the
old got fed up with your bullshit.

3\. Accept government contract to move 10 messages from webservice to a
mainframe. Ensure you need 5 different features when processing moving the
data. Each of the features requires, of course, their own programming
language, database technology and their own message format. Use 20M (yes,
that's twenty million) LoC. Yes, I have worked on a contract to add new
functionality to something like that (10 messages with two dozen fields to
move from ASP.NET service to IBM mainframe. 20M lines of code. Polish social
insurance administration)

~~~
pestaa
How do you begin a complete rewrite without stakeholder approval?

Please advise.

~~~
lmilcin
It depends on the organization. Most teams in most organizations have enough
freedom (and rightly so) to do some kind of cleanup work, improvements,
refactorings. When the development team is unsatisfied with the state of the
application and they feel they will not get buy in for rewrite (and usually
rightly so...) they might decide they will rebrand it as internal
improvements/cleanup/refactoring. Have seen it many times.

~~~
pestaa
Ew, that's a deadly dose of office politics waiting to blow up.

Guess I haven't read the guide on how to make a toxic & dysfunctional
workplace.

~~~
lmilcin
To seriously think about creating unmaintainable masterpiece you must ensure
correct environment that will meet the challenge.

------
yyyk
The link seems a bit old, modern day development has added many new and
exciting methods for making unmaintenable code:

\- Use exceptions for control flow. With different exceptions leading to
entirely different flows. On C you can use setjmp/longjmp + globals for a
similar effect. This will help the maintenance programmer develop thinking in
multiple dimensions.

\- To save space, do not declare separate constants for every magic
number/string. Instead use one large constant pooling all the constants, and
then deduce the other constants from parts of the one large constant. You'll
be teaching the maintenance programmer an advanced optimization technique.

\- Abuse localization rules whenever a case-insensitive operation is operated
(e.g. Turkish i, German eszett, etc.). You'll be broadening the mind of the
maintenance programmer, teaching cultural differences.

For extra points, when dealing with fixed string constants in the code, abuse
Unicode's right-to-left rules to make the shown data rather different than the
actual data the compiler sees, and zero width characters the make the shown
length different than the actual length. This is particular useful with the
"one large constant technique" from above.

\- Do use locals when the locals shadow the global variable. This will teach
the maintenance programmer to pay attention.

\- Supply more precise overrides in files/projects included in partial
compilation as to make code that will behave differently depending on
compilation order, included files/projects, etc. This will help teach the
importance of a build system.

Bonus points if the included code is actually similar, but relies on subtle
differences in the behaviour of a previous version of the framework or
included packages.

~~~
mcknz
I saw a version of this dating back to 1997, so that's why it probably seems
old.

~~~
mttjj
The second to last paragraph states that the author gave a version of the
document as a talk at a conference in 1997. So it appears that your memory is
spot-on.

------
dang
These seem to be the interesting previous threads—let me know if I missed
one...

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

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

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

2015 (different article, same title)
[https://news.ycombinator.com/item?id=10237636](https://news.ycombinator.com/item?id=10237636)

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

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

2012 (a bit)
[https://news.ycombinator.com/item?id=4717912](https://news.ycombinator.com/item?id=4717912)

2010 (a bit)
[https://news.ycombinator.com/item?id=1573034](https://news.ycombinator.com/item?id=1573034)

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

As for the year, 2003 is an upper bound given
[https://web.archive.org/web/20030626161523/http://www.mindpr...](https://web.archive.org/web/20030626161523/http://www.mindprod.com/unmain.html).
Open to lower suggestions.

------
axegon_
> Names From Other Languages

This is the worst thing that you could ever end up dealing with. Back at an
old job many many years ago, the company bought a smaller one with the same
business. Me and my at the time tech lead were given the task to migrate the
database from the system they were using into ours. "How hard could it be" we
thought. Well... All 51 tables scattered around two databases were in
Romanian. Needless to say neither of us knew a single word in Romanian. I was
surprised how much we managed to learn in a month though.

~~~
arkitaip
I write all my personal code, including comments, in English and not Swedish
precisely because I want non-Swedish devs to be able to perfectly understand
the code even without Swedish language or cultural knowledge. Also, thinking
and writing in English is much simpler than jumping between two languages.

~~~
throwawaynothx
Personally, I don't write comments. I write self explanatory method names.
Comments become unmaintainable

~~~
IggleSniggle
Regarding the relative usefulness of a well-named method vs an explanatory
comment, I think it depends on the culture of refactoring for the codebase. If
a codebase sees its code very regularly refactored, then comments can be
downright dangerous/misleading while methods are easily renamed throughout. On
the other hand, a well placed comment can tell you a lot of “why” or “how-to”
that a method name / signature cannot.

------
temporallobe
I know this will get downvoted into oblivion on HN - but I just can’t resist.
Another way to write unmaintainable code - write it the Ruby/Rails way! No
comments, no flowerboxes, no docstrings! Your method and variable names ARE
your documentation!

In all seriousness though, I have found that even with carefully and
thoughtfully named variables, classes, methods, etc., code with no comments
and no human-readable explanation is very difficult to maintain. I’ve been
doing it for years and it never gets easier, especially with large code bases.
Yes, the ROI for adding useful comments may seem low now, but it does pay off
for the unlucky newb, intern, or junior engineer who’ll be stuck fixing that
tricky bug or integrating that odd feature request in the future.

~~~
igornadj
Can you elaborate? What are the juniors stuck on? Usually if a junior doesn't
understand something it means the naming/structure is not as clear as you
think. One of my favourite things as a senior is having a junior peer review
my code, the places they have questions is a great sign the change needs work.

~~~
temporallobe
I didn’t mean stuck “on”, I meant stuck “with”. I say this because juniors are
often tasked with fixing the odd, seemingly easy bug, but if they don’t know
the code base well, and especially if it’s not well documented, it could be a
challenge, often requiring the seniors to work with them.

------
29athrowaway
There are "programming antipatterns" that can be intentionally used to create
unmaintainable code bases... like magic numbers/ strings, obfuscated spaghetti
program flows, etc.

But there are more unintentional ways of making your code base less maintable:

\- Lack of consistency: Avoid using multiple different words to describe the
same thing.

\- Mix everything together: This happens when instead of having a dedicated
piece of code for each purpose, the code contains functions that tries to do
everything at the same time.

\- Cyclomatic complexity: This happens when a function can have many different
unique outcomes, making it hard to understand and test. Long functions usually
fall into this category.

\- Implicitness: Being implicit can make it very hard to figure out what is
going on. Abbreviations and acronyms fall into this category as well. Bonus
points if you excessively use operator overloading, reflection or anything
that can be used to have a layer of magic happening.

\- Messy concurrency: If you are going to be using concurrency primitives, you
better have zero tolerance with messy code... otherwise, you'll be living in a
multithreaded hell made out of spaghetti involving mutexes, condition
variables, semaphores where things break and you have no idea why.

\- Memoirs about journeys to nowhere: This happens when instead of writing a
comment explaining what something does leaving out irrelevant details, you
write a longer comment that reads like a monologue with many irrelevant
details where the central point is you rather than the code being documented,
and reads in a complicated, non-linear way... like: "I thought this did A, but
then because of B, C happens this does D. Right? TODO: find out more about E".
Some people think this makes them look clever.

\- Interleaved levels of detail: Instead of having layered levels of detail
that do not mix, write code that deals with high level things and very low
level things at the same time.

------
nirui
> Single Letter Variable Names

I saw @axegon_ talking about "Names From Other Languages" and calling it
"worst thing that you could ever end up dealing with". But I tell you, a
"Single Letter Name" master will turn your mind really quickly.

You open up the code, that's a file of over 2,000 lines, no comment. Everybody
inside there were called `a`, `b`, `n`, `i`, `q`. Some functions were a little
better, `handle`, `add`, `equal`, `DataClass`, `MainProcessHandler`,
`Service`.

Yeah, I rewrite the whole thing because at least I can trust the stupidity of
my own.

~~~
Cthulhu_
Weirdly enough, Go seems to actually promote short / single letter variables,
depending on context: "The basic rule: the further from its declaration that a
name is used, the more descriptive the name must be. For a method receiver,
one or two letters is sufficient. Common variables such as loop indices and
readers can be a single letter (i, r). More unusual things and global
variables need more descriptive names."

[https://github.com/golang/go/wiki/CodeReviewComments#variabl...](https://github.com/golang/go/wiki/CodeReviewComments#variable-
names)

------
pmontra
An example of bad naming from some code I inherited and worked on last week

    
    
      if check_date(owner):
    

actually meant

    
    
      if has_an_active_subscription(owner):

------
martopix
I'm just here to say that the plural of status is not "statii", not even in
Latin. Status is a noun of the (relatively rare) fourth declension, and its
Latin plural is "status" itself.

------
avinoth
One other excellent way to throw off the maintenance developer is to define &
use LOTS of dynamic methods in obfuscated locations.

Step up your game by defining those methods from a DB entry.

(I wish I was making this sinister plan up for satirical purposes)

~~~
IggleSniggle
Well of course you should define the methods in DB entry, that way _they can
be changed as needed as business needs change_. Seeing as nobody is going to
be touching the absolutely sacrosanct source code, putting functions in the DB
and reading them into the code base during compile and/or hot-reload is really
the only reasonable way.

------
a_imho
Surprisingly light on version control best practices.

1\. Big commits make it easier to evade code reviews, submit pull requests
close to the deadline

2\. Commit messages should be generic and misleading

3\. Logical units of change should be split up between many non consecutive
commits, big commits should contain multiple different features and bugfixes

4\. Have bots commit to version control frequently

5\. Keep multiple copies of the same files for backup

6\. Rename and move around files frequently

7\. Reuse filenames liberally

------
AnonHP
One thing not mentioned here is using regular expressions. Use regexes
liberally to process all data [1] because eventually it will converge to it
and if you haven’t looked around for a solution involving regexes, [2] you’re
not working on the really hard problems. Oh, don’t bother writing comments
about regexes. They’re pretty much self-explanatory once the expression itself
is beyond 10 characters long.

If regular expressions seem too tame to you, write all your business logic in
SQL that’s contained in a long stored procedure. Prefix the name of the stored
procedures with “HBD_” (HereBeDragons) so that your future self doesn’t get
hurt. For added pain, use triggers that will invoke stored procedures, but not
everywhere. In this case, inconsistency is the name of the game.

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

[2]: [https://stackoverflow.com/questions/1732348/regex-match-
open...](https://stackoverflow.com/questions/1732348/regex-match-open-tags-
except-xhtml-self-contained-tags#1732454)

------
klyrs
SFINAE is the obfuscator's best friend. In c++, you only need a single
function name. You'll need to add garbage parameters to disambiguate some
calls, but that will only add to the reader's challenge.

~~~
edflsafoiewq
ADL is nice too. For best results, spread each namespace over as many files as
possible.

------
JMTQp8lwXL
Using an underscore for a variable name is perfectly fine when defining
callback functions, where the underscore merely fills the void for the 1st
parameter name, and you only need the 2nd parameter.

~~~
rfw300
So naturally, one should be sure to use underscore variables in all contexts
except that one.

------
vsareto
It doesn't matter much how much work you put into maintainability, someone
somewhere will find a rule to declare it unmaintainable.

The rules for maintainable code are not maintainable!

~~~
lmilcin
I would agree if this was about "bad" code. But maintainability is easy to
prove -- just give some random developers a feature or two to implement and
see the results.

In every single project I ever joined there were people complaining about the
quality and maintainability of the code. Yet some of the projects were truly
stuck not able to deliver anything and others were chugging features at a
steady pace.

Maintainability is difficult to define but it is definitely real.

------
booleandilemma
Java? Pfft.

Step 1: write your project in Node.js.

~~~
gonzo41
Came here for this.

------
wodenokoto
> Never ascribe to malice, that which can be explained by incompetence. -
> Napoleon

I have never seen that quote attributed to Napoleon before.

Today it is known as Hanlon’s razor, but it can be traced quite far back in
time, just not to Napoleon. As always, Wikipedia got you covered:

[https://en.m.wikipedia.org/wiki/Hanlon's_razor](https://en.m.wikipedia.org/wiki/Hanlon's_razor)

~~~
de_watcher
I this case it was probably neither malice nor incompetence. They've probably
attributed the quote randomly just for fun.

------
Seb-C
In modern development you should actually use micro-services. It is so useful
that you can use it for everything. For example, if you find out that you have
too many different functions to validate an integer, write a micro-service
that validates integers!

And because the new service crashes when you input a non-integer string,
please catch and ignore the exception client side (we'll fix it later).

~~~
Cthulhu_
Definitely don't spend any time on things outside the scope of your
microservice, your code is wonderful and pure and any pesky architect that
wants to argue about integration with the rest of the architecture just
doesn't appreciate your Art.

On that note, as architect make sure to insist on perfect implementations of
specs, like the JSON:API media type
([https://jsonapi.org/format/](https://jsonapi.org/format/)), or use older
formats like XML and insist on perfect implementations. A simple "validate
number" microservice will have so many nonfunctional requirements it'll take
years to develop.

------
Cthulhu_
I've inherited a codebase that seems to have taken this article to heart. It
uses random unnecessary abbreviations, reuses variables like "i" within the
(function) scope (JS) of >1000 line functions, it's got some 15K line files,
comments that mean nothing, copy / paste everywhere, it just goes on.

------
grumpy-cowboy
How to write unmaintainable code? Easy : write a project based on the latest
"saveur du jour" JS framework.

------
eNTi
I believe that the current trend to decouple everything is one way to make
something a maintenance nightmare. Using reflection or micro services to tie
everything back together. Data shouldn't flow between components without some
black magic fuckery ever or you could have hard dependencies that can't be
decoupled easily and split into more testable functions and seperate files.

For good measure do it in TDD. That just creates the cherry on top for
maintainability in high pressure scenarios where features have to be generated
quickly ... and possibly not by the same dev doing previous work and is used
to this high powered mode of existence.

Whatever happend to finding balance between coupling and cohesion?

------
crgwbr
> Hungarian Notation is the tactical nuclear weapon of source code obfuscation
> techniques; use it!

As a young programmer I worked in a very serious enterprise codebase that used
the systems variant of Hungarian notation: prefixing variables with str, int,
bl, fl, etc. They also sometimes prefixed for scope, like g[lobal], m[odule],
etc. To this day I don’t know if anyone on that team of people 20 years older
than me understood why I had such a tough time not cracking up in meetings
discussing ‘gstrNotFoundErrorMsg’.

------
umvi
Woah, do compilers really have function name length limits? Which languages?

~~~
samatman
The ANSI C standard requires a compiler to ignore any characters in a variable
after the first 31.

~~~
hvdijk
This is not true. It requires compilers to support at least 31 significant
initial characters in external names, 63 otherwise (in C99), but beyond that
it is a quality of implementation issue and implementations are explicitly
encouraged directly from that same standard not to impose artificial limits.
The C standard allows and recommends compilers treat the full names of
variables, functions and anything else as significant.

~~~
samatman
Ah, good to know, I thought it was proscriptive.

Let me rephrase, then: to write portable C, you must keep the first 31
characters of any name distinct, because it's legal for a compiler to ignore
everything after that.

~~~
hvdijk
Yup, phrased like that it's about right and a legitimate gripe. The limits in
C90 were even worse. There are still a few projects around that use the
requirement to treat more characters as significant in internal names to work
around that, like so:

    
    
      #define copy_from_memory copy1
      #define copy_from_file copy2
    

And then provide the declarations and definitions of copy_from_memory and
copy_from_file as normal, knowing that they will be renamed to copy1 and copy2
by the preprocessor.

------
splittingTimes
Previous discussion

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

------
hackerm0nkey
in short, grab a task. make it work, and never look back to make anything
right.

------
shultays
(c++) template everything

------
dheera
I'm pretty sure there are lots of people in the industry who write
unmaintainable code _on purpose_ for their own job security. I don't endorse
this, but I've heard horror stories at several well-known companies about
instances of this.

~~~
rightbyte
Probably not working very well since the bosses can't care less about the
actual technical quality when making decisions. I.e. they don't know or care
it's unmaintainable. So you build your fort and get picked off just outside
the gates.

It might be revenge though, not job security? Or maybe "organic" local job
security, i.e. the colleges of the perpetrator can't take over his tasks
easily without management investment?

I guess most bad code is just bad/novice programmers or bad project
management.

~~~
johnlorentzson
"I'm the only one who can understand this important code" seems like good job
security to me

------
jaimex2
Just make and use your own libraries in your own private repos that can't be
switched out easily. Then just revoke access as needed.

