Hacker News new | past | comments | ask | show | jobs | submit login
How to Write Unmaintainable Code (1999) (ic.ac.uk)
159 points by dhuramas on Sept 18, 2015 | hide | past | favorite | 70 comments



They are missing the creatieve use of your version control system.

First, make sure none of your commits actually build out of the box; forget a file for a few revisions, accidentally check in a corrupted XML file, etc.

Create a "trunkV2" directory, and keep committing to both trunk and trunkV2. Add a third trunk inside the "branches" tree (/branches/featureX/newTrunk; I have seen this n practice, with random customer branches sprinkled in)

In the tree of the service that your program calls, do something different, for example by starting with a simple tag 'V1 frozen', adding the revisions of the V2 version and then occasionally unfreezing the V1 one by committing a combined "undo everything since V1" and "tiny fix to the v1 protocol that we thought was frozen", followed by an "undo that, and do something else" commit. Make sure to comment those commits as "version for customer X" and "bug fix #3 for customer Y"; do not mention "V1" or "V2".

Also, make sure that "customer X" has both V1 and V2 deployed, so that future developers cannot learn that "customer X" implies "version 2".


One I saw happen: avoid merge conflicts by having each developer maintain their own fork of trunk, or better still, multiple forks. Copy and paste is easier than merge anyway. Automate builds for all these, and provide a way to release any of them, without recording which you used. Never, ever tag. You want to delete branches too? Sure, why not. Of course this makes it hard to track dependencies, so be sure to check in the binaries of the projects you depend on into your project, especially if those were built on your own machine from source you haven't checked in instead of builds from the build server.

(and when I say 'saw happen', I mean 'I spent a week in Kiev at 12 degrees below freezing trying to figure out where the source code had gone'...we never found all of it)


In Subversion, I've seen branches created, deleted, and then another branch created with the same name from a different subtree of the repository.

I was also successfully able to export all of this to git with every deleted branch now still in existence.


I definitely like the git workflow more... branch, work, rebase, PR, review, rework, rebase, review... merge.

short change cycles are easier to reason with.


Also a nice thing to do is to split the project into multiple repositories, each also split into multiple branches.

Then when a dev checkout a branch in one repo, they need to find and checkout the matching branches in the other repos in order to build the project.

To make it more fun you can name the branches slightly differently from one repo to another, like "mybranch3" in one, and "my_branch3" in another.

Sometime a feature only affects one repository. That's fine - just create a branch there, and leave the other repo to the default branch - "master" (or sometime "release", unless it's "current_test", or maybe "dev-temp", or...).


You can also use a version control system like ClearCase that doesn't have the concept of an atomic commit of multiple files. Work on several features at once and check in the changes in a random order. Make sure not to leave helpful check-in comments.


Also make sure your build system only works with a specific combination of Windows XP, cygwin and ActivePerl.


Branching:

Copy folder X, paste it and name it 'X-branch-<date>'

Make sure that <date> is not in the same format as any previously used, if you run out of formats, change the ordering or add your name, e.g.:

'140215-kraftman-MAIN-BRANCH2'


Within the same repo, have two branches: database and Database and alternate between them.


Yes, I'm sure that git can irrevocably destroy any source code, you don't even need to know anything about git for that. As an added bonus, you can have as much differently broken “repositories” of the same project as you like.


Also, use git rebase instead of merge.


They've missed the liberal use of dependency injection, triggers, or anything that can create a side effect of an event without being documented in the function being executed.

Much fun to be had there. Even just setting up PostgreSQL partition tables can defeat the most diligent maintainer (as they suddenly find RETURNING id_column fails everywhere).


There's only one rule:

1. Don't write fast running behavioural tests that verify the software is working as required.

With fast running tests I can maintain any old crap. Well, technically speaking I can rewrite it, but no-one will mind.


Write tests that ensure each subroutine is called with specific parameters, so that functionally identical modifications cause the test to fail. Any change to the code now requires a change to several tests.


Isn't that what TDD is all about? The logical consequence is code that vitally depends on the existence of specific tests.


TDD will leave you with a load of tests which prevent you from changing the code. BDD on the other hand should leave you with a suite that isn't coupled to a specific implementation.

Part of the information contained in a test is the intent of the verification being performed, and part is code for mapping that intent onto the implementation. The latter part can be discarded during a rewrite. Cucumber/SpecFlow etc. make the difference between these explicit.


> and part is code for mapping that intent onto the implementation

Wait, what?! The more I listen people talking about TDD, the more I discover I always misunderstood it, and the less I want to actually understand it.


The thing is, unit tests that are affordable to implement, are not wort it. Their objectives should rather be verified with language choice, programming techniques, and assertions. Tests that are worthwhile to write are those that test interactions of multiple subsystems. But those are not unit tests any more. If you end up _actually needing_ TDD, then that proves you are using the wrong language and/or the wrong technique.


> Well, technically speaking I can rewrite it, but no-one will mind.

Try that on multiple teams across timezones actively developing multiple branches, with big merges on a weekly basis.

"Rewrite module X" never happens. Even if you have a healthy suite of behavioral tests, it will still feel brittle because rewriting large sections of code will inevitably bite you in the ass.

And no you can't say "you're merging main into your branch? your responsibility."


"Assuming a perfect test suite, ..."


"Hugh McDonald, hughmcd@ican"

The guy has an e-mail address hosted at a top level domain. 1999 was a silly time.


That is crazy.

I thought I was doing well to have my name be a valid domain: https://david.kitchen/


I've thought a particularly effective approach is actually to do the "right" thing but take it to ridiculous lengths - the "Enterprise FizzBuzz" that was posted on HN recently being a good example of this.


Use all of the techniques described in the GOF book at the same time.Your flyweight should collapse because the facade was too heavy with decorators to support another observer.


I'm not sure that's the right thing on any level...


Also a later version at http://www.stateslab.org/HowToWriteUnmaintainableCode-Green0... which includes that crazy offbeat language, JavaScript (which was, amusingly, my contribution).


> E.g. on the screen label the field "Postal Code" but in the code call the associated variable "zip".

If you support multiple countries this is probably an inevitability. Same with province vs state, potentially others.

I guess you could have one variable called "postalCode" and one called "zip", but that seems potentially more confusing.

Also, sometimes your UI shifts - should all your code change then? Because the boss wants to call something different in the UI? "We were calling them admins, now we want to call them CSR." Is it time to write a regexp to rename everything?


Adding... for web applications use IDs and Classes liberally in the html output... for actual string injection, especially if you support internationalization, this is needed so you can separate the text to display from the controls in place.


I had Susan as a tutor in my first year at Imperial. This made me chuckle.


Same here ;-)


My favourite one must be number 20. Best ever quote of Thumper I've seen...!

"Never document gotchas in the code. If you suspect there may be a bug in a class, keep it to yourself. [...] Remember the words of Thumper "If you can't say anything nice, don't say anything at all". What if the programmer who wrote that code saw your comments? What if the owner of the company saw them? What if a customer did? You could get yourself fired."


How about obfuscated MUMPS?

P R I N T S (A,L)=1,(S,T)=I N G G I V E N A O F A=L:L:S Q:U=A R E S B=E L O W (A*A),! I S (U,R,E)=T H 1 N K I T=S G O O D


Does readable MUMPS even exist?


Yeah but it's not proper style.


And yet give me a code base with clear contracts, narrow interfaces and minimal dependencies with EVERY ONE of these so-called 'violations'...all of which are are trivial to contend with if you have a good software architecture.

Am wondering when our industry will see this forest for the trees...


While the advice in the article is funny, on a deeper level the author seems to argue that program text is a rather basic representation that often obstructs insight into the semantics of the program.

The quest for other program representations is still open. The state of the art uses text as "storage" format plus an IDE that does some semantic analysis to help the developer navigate. Many of the ideas mentined in the article (navigation, coloring, auto-format) have been integrated into IDEs and editors.

With almost 20 years of research between the article then and now, what semantic techniques are you dreaming of in your development environment? What are you missing? What feature would greatly improve your productivity (but is possibly too costly to implement by yourself)?


Also relevant The Selfish Class[1]. How to write classes that thrive in the struggle for programmer attention.

[1] http://www.laputan.org/selfish/selfish.html


> You earn extra Brownie points whenever the beginning and end of a block appear on separate pages in a printed listing.

I'm aware that this is from 1997-1999, but I'm curious: Do people still print out source code for better reading?


The essay was presented half-seriously as part of a module on software maintainability [1], which I took in 2007. Perl was given as an example of a write-only language: modification of a typical Perl program requires at least a partial re-write. (The extra joke was that many of the computing department's internal systems were written in Perl.)

More seriously, there were discussions of library versioning schemes, backward compatibility etc.

I see Susan no longer lectures, which is a shame, as she was one of my favourite lecturers.

[1] https://www.doc.ic.ac.uk/~susan/475/

(The replacement course seems to be this: http://www.imperial.ac.uk/computing/current-students/courses... )


I fondly recall her design patterns course, way back in '96!


Still, when two writes of a Perl program is less code and time than one write plus one modification of a C program, Perl still looks good.


I tend to do it whenever I'm working on something I find tricky.

For me it's much easier to focus if I take my printouts and get away from the computer and edit and write code by hand, then go back and type in the changes.

I started doing this when I thought back to times I programmed back in high school and college and remembered that I'd always had the most success with programs working correctly when I'd written them out by hand before typing them in. I think it forces me to really think about what's happening, rather than just trying stuff until things "mostly" work.

I find that when I'm sitting at the computer, I have a much more iterative style, where I try something, see how that went, and make changes. When I'm working out something away from the computer, I have to have a coherent mental model of what I'm trying to accomplish.

So at the computer is good for experimenting and exploration, away from the computer is good for coherent designs.

I think there's something analogous with languages, I find dynamic languages much easier to deal with when I'm sitting at the computer, but if I'm working away from the computer on paper, then I prefer languages like SML or OCaml (or maybe even Go?) where I can look at the code and work through in my head exactly what is going on.


I was watching a TV documentary a few weeks ago about a botched government IT project, and a company that also had bad experiences with the company that did the project had some British auditor review the software that was delivered for their project. The show interviewed the auditor and he actually pulled out a box (maybe 50cm/20 inch high), completely full, with printouts of the whole source code of this application. He was showing his notes in them, it seemed like he actually went through and annotated 4000-6000 pages (if I estimate it from comparing to the height of a pack of printing paper which has 500 pages) of printed Java source code.


To be fair, an IDE is not geared at auditing and it would be much more painful to do freeform annotations in it. Circling problematic areas, drawing connections between variables/functions, making handwritten notes in the margin... all that is easy on paper and requires workarounds/other programs on a PC.


I can see that, but how does one work through 5000 pages? I'd shoot myself in the head before the end of week 1 (metaphorically speaking). And I guess they used a combination of digital/analog methods, but still since the GP asked - that guy must've spend weeks studying printed source code...


As I see it, the auditor's job is to analyse the system thoroughly, not to skim some areas of it. Looking through 5000 pages worth of code on a computer screen would also be quite a challenge, even with the interactive aids you get from an IDE.

Tangent: speaking from personal experience, sifting through an existing foreign code base is a really exciting task for some people, figuring out what makes it tick and how the pieces fit together can be fun. Granted, I was reverse-engineering a closed-source product, not reading its actual source, but that was even more fun :D Come to think of it, the tool I used for that, IDA Pro, has some interesting features that IDEs don't, such as graphical representations of function call graphs [1] and function basic blocks [2]. It would be interesting to see what a creative person could do by integrating those into a regular IDE.

[1] http://scratchpad.wikia.com/wiki/Reverse_Engineering_Mentori...

[2] https://www.hex-rays.com/products/ida/tech/graphing.shtml


    > Do people still print out source code for better reading?
Can be required, academically. (Where "better reading" means "how someone's decided they want to read it"!)


If you have a lot of code printing it all out and laying it out on a spare desk - so you can look at all the code in one go is still useful.


I haven't recently, but I have 4 monitors at work. I use emacs with follow mode and multiple buffers opened so I can see lots of text at once. But when I only had one monitor, I printed a lot of things out. The difference is like trying to drive a car in the winter with only a 1'x1' patch of clear windshield versus having the entire view.


I did that at my first full-time development job. There was a 50 line method to refactor and it was making calls to some other methods so I printed maybe 5-6 pages of code. It helped to get away from the computer because I could focus only on the logic that I was refactoring rather than worrying about making tests pass or seeing it work in a web browser.

It's easier to solve a problem when you remove all the fluff and noise. On the other hand, you won't know if you have a good solution until it's implemented. A bit of a trade-off.


My colleague does this and he is the main user of our office printer by far. As a driver writer, every now and then he'll print a few parts of the hardware source out in minute font and proceed to annotate/highlight it. He then files it away under his desk in the folder for that version. I'm happy in the knowledge that should our fileserver and backups spontaneously explode, we have a semi-versioned copy ready for someone to just type back into the computer. But in seriousness, it seems to work for him.


> I'm aware that this is from 1997-1999, but I'm curious: Do people still print out source code for better reading?

I know a bunch of embedded C programmers who have been doing it for a long time who do this regularly. They know their stuff. They don't print out the whole codebase, just a few pages of whatever they're working on, then they annotate it with a pen while thinking about whatever problems they are solving.


I can imagine this being more helpful if you lacked access to a debugger, and lacked the time (or desire) to build debugging tools by other means... Which I imagine is plausible in that context.


No, they have debuggers and modern dev tools. They just prefer to read code from paper when they have to really focus on it for a long time.


In my experience, it's less about reading code on paper than it is the other thing you mentioned:

>then they annotate it with a pen while thinking about whatever problems they are solving.

Paper and pen offer a lot of options you don't have in vim (or emacs/notepad.exe/etc ;)). Circling, doodling, arrows, underlining, furiously scribbling...


Just happened, working on a new codebase. I never print more than ~5 pages but it helps me organise the codebase in my head when it's a complex one. As an alternative, I also write down code flows. I always keep a notebook by the laptop.


I am a mechanical engineer and lots of my friends have a thesis where appendix 3 is a bunch of printed MATLAB code. Of course in this case it doesn't matter because no one will ever read it.


Yes. Well, relatively recently. I did it just a couple of years ago.


Look! Responsive Web Design from 1999!


This should be renamed to: "Common red flags to look for in merge requests and code reviews" and handed to every rookie, mid-level, and senior developer on your project!


Depending on the team you can get away with some of these. Have a manager/PHB that actively discourages unit testing? You don't need to commit your unit tests or write them at all! No one fixes an instance of copy/paste coding? Great, now you too can be efficient by copying and pasting everywhere. No one uses a code formatter or uses different formatting styles? Good! Now your style can shine!

These are all good red flags to check for in existing code bases before you join a team. If you see them, chances are their code review process is poor or non-existent and you'll be fighting a battle against crap code every single day.


Looking through the codebase for these to assess the teams review health is a fantastic idea!


I once worked with a programmer who would not indent any of his code. "It makes it go faster" he said. Wrote all of his code in Windows Notepad too.


I've found it helpful to write several functions with the same name throughout the codebase. Also, when refactoring code, don't rewrite it completely, just clone the directory and append "_new".


Writing unmaintainable code increases job security.


You have to get away with it for a while first. Otherwise you get fired before you are the only one that knows what is going on.


"Never put in any { } surrounding your if/else blocks unless they are syntactically obligatory."


genius! :)


use perl


javascript is OK too. Avoid Python.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: