Writing enterprise software is hard. In my experience, a single project can simultaneously have competing stake holders, unrelated distribution chains, and shifting requirements. Luckily, software development isn’t the only industry that faces these problems like this. Inspiration can be found everywhere.
This week I found inspiration while reading an Atlantic article about Late Night with Seth Meyers. Apparently, Meyers has been shaking up the Late Night scene by tinkering with some of the Sacred Cows of his genre. When explaining the significance of his changes Meyers said, “I don’t think these are traditions as much as they’re structures that have proved they can bear comedy weight. That’s the reason people keep using them.”
Meyers’ appeal to demonstrated utility over philosophical or historical arguments is a really smart way to approach complex systems, where changes often have unexpected consequences. With this in mind, here are some of the structures that have proven their ability to bear weight in my software projects.
ETL: Perhaps the best decision we ever did was to use the ETL pattern for a few early modules. In our .NET projects that meant we made three interfaces: IRead, IMap and IWrite. We have been able to reuse these interfaces again and again in all kinds of application contexts. These interfaces have become so common that I even find myself writing simple HTTP Request handlers as IMaps, a habit that hasn’t hurt at all and has, on a number of occasions, helped to migrate front end reports to back end scheduled jobs.
URL: The first time I used this pattern was to reference a specific database table. Before using a URL we tracked three separate data points: Server, Database and Table. With the URL we were able to consolidate these three points into a single URL. Our URL used a custom ‘table’ scheme with the following format table://
/ /<table>. We now have many custom schemes used all over (e.g. user://, email:// and grid://).
Unit Tests: I know everyone says it, but I am still amazed by how many projects I’ve worked on without them. We knew that we’d be supporting our applications for years so we took the time to build an entire library just to make tests easier to write and maintain. It has paid itself off a hundred times over. At least five times a day (the approximate number of times I run the tests) I think to myself, “thank God we wrote these.”
Universal Data Transfer Objects: As with Unit Tests, this one took a moderate amount of upfront work and only paid itself off over years. While moving information around the system (from the database, to the web server, into cache or wherever) we started to create several small one off Data Transfer Objects that had no function other than carrying data. To remedy this we decided to create an IRow interface that was basically a simplified dictionary. We then created type converters that allow us to easily convert any object to an IRow representation and back again. No more one off DTO’s needed. A secondary benefit was optimizing and unit testing our IRow implementations, making our code highly performant and dependable.
Composition over Inheritance: This one I got from the good old Gang of Four. Not much to say here other than we often find ourselves regretting decisions to use inheritance. There have been times when inheritance has been nice but those times are rare and usually involve incredibly simple classes (like the URL’s mentioned above). To avoid inheritance we have relied heavily on the Utility libraries mentioned below.
Utilities (aka Shared or Tools): A wise developer once told me, “not everything has to be an object. It’s alright to have some one-off, static utility methods.” When we are running full speed towards a deadline throwing quick helpful functions into a shared pool can be a life saver. After some time has passed we will come back to our utility methods and, with enough of them in one spot, are able to find ways to organize them into proper classes.
These structures have become good friends, saving me time and time again from unanticipated design-problems. Unfortunately, patterns, no matter how useful, can also become a hindrance by becoming boring, a motivation killer, or a crutch, a creativity killer. To avoid these traps Meyers had one more piece of advice: find non-critical outlets for experimentation. In this way a developer can always be improving their craft without risking their livelihood.