I met Patrick Rhone at the Minimal Mac meetup this week at WWDC. All in all, it was my favorite thing I’ve done this week outside of the convention proper because it had a much more personal feel than any of the other convention parties or meetups that I’ve had a chance to attend.
When I described my current project at work, when I got around to the part about code generation, Patrick responded “So it’s a hack.” It was good natured teasing, but like a lot of humor, it’s funny because there’s a kernel of truth to it.
This got me thinking on just what it is that separates a “Hack” from a “System.” I came up with what I think is a reasonably good list of traits that define that seperation, and I’m going to be honest and grade the code generator on them. I also plan on using this as a punch list of things to fix going forward.
Hacks tend to be poorly documented, assuming they’re documented at all. One of the defining features of most of my favorite systems is that they’re well documented, both in terms of structure and function. Documenting structure defines the blueprint to build around, and documenting function makes the system easier for people who didn’t build it to understand it.
I give myself a 4 out of 5 for documentation, I wrote a complete spec for how the code generation functions, how to write new sections for it, why I made certain design decisions, and the general structure of the code. I need to go through more of the lower level classes and add better commentary to the code, but I have a solid foundation for documentation.
This is all about external dependencies. Most projects have external dependencies,at the very least your programming language is a dependency, and the hardware you run on.
There’s also a scale of safety in dependencies, are you using a modern version of a popular language? That’s relatively safe. Are you depending on an undocumented API in the OS that’s known to change without warning? Not safe. Running an old but still popular version of a server? Somewhere in the middle.
For my own project, I’m writing the generator in a modern version of Ruby, and it outputs Objective-C and Java code that’s formatted to be human readable and follows style guides, and generates no compiler warnings or errors. I’m using a few gems, but I check to make sure they’re currently supported projects with at least vaguely active communities. Since I’m leaning on 3 programming languages, I can’t give myself a perfect score, but I’ll call it pretty good and say maybe 4.5 out of 5.
This is something of an outgrowth of documentation, but the documentation only implies an intent to be consistent. Sticking to that documentation and following a style guide, naming conventions, and coding conventions in practice can be much harder than writing the plan on how you’re going to do it. This is important internally in your project for future additions and maintenance, and externally in your API, interface, or document formats so that people interacting with your project can understand it.
For my own project I really nailed internal consistency, but internally things are less than ideal in terms of internal consistency. For all of the external facing sections of the project, there are well documented formats for the input, and it’s agressive about crashing with a useful error on bad input so fixing the error is relatively quick. Internally, while I worked to tune the templating with ERB to make as much sense as possible, it still feels disconnected from the ruby code that generates the fate that fills in the ERB forms. I also need to do another code audit to double check that the structure of my earlier code is up to par relative to my later work. Overall I’ll give myself a middle grade of about 3 out of 5.
I’m not referring to stability in terms of not crashing, but rather in terms of having a consistent, roadmap. Hacks change radically and unpredictably. Systems change slowly and give you advance warning or solid guidance based on what will happen next. Things like grace periods and warning on deprecated methods, documented support windows, and API versioning with backwards compatibility seperate the hacks from the systems.
Grading my project, I fail pretty hard at this point, because I’m the only consumer for my code generator right now I’ve been lax implementing this. I’m giving myself a 0 out of 5 on this one, and putting adding version checks and branching the code in git based on those versions onto my to-do list.
Much like consistency, this is tied to the documentation. Breaking large projects into smaller loosely coupled chunks keeps them maintainable and makes it easier to make changes down the road. This also should help keep code healthier by making you think about how the layers are built, and how to seperate them intelligently.
I can give myself a good grade there because I forced myself to do a major and unpleasant rewrite of most of the project when I realized in my desire to get version 1 running I had gotten lax in my layer design. The major version 2 rewrite has much stronger modular design and even separates some of the sections into locally built and packaged gems rather than .rb files in the same project to force me to maintain proper layer separation. I’m sure I could do better, but I’m now mindful and have a solid definition of the layers and functions in my project, so I’ll give myself a 4.5 out of 5.
Stewardship / Ownership
Stewardship is a very fuzzy concept when it comes to software. While the NSHipster article I’m referencing is in reference to open source projects, this also matters for internal projects. Who “owns” a project and determines the direction, disposition and status of it is important to consider. It’s tied to stability, but it’s the human side of it that generates the long term plans.
For my app, I’ll give myself a 3 out of 5 as I’m committed to the project, and it’s actively maintained. On the other hand, there isn’t any sort of formal succession plan, and it’s an indirect responsibility as the system supports my primary responsibility to my employer, but nobody but me considers it a formal requirement. If requirements for the project that it supports were to change significantly, I could see making the call to scrap this project and either write a new code generator, reduce the scope of code generated, or switching to writing the code by hand and using unit testing to make sure that both sides of the API align properly.
Overall the project averages out to just over 3.15 out of 5, largely dragged down by the failing grade on stability. This makes it less of a hack than a system, but not by a whole lot, and is slightly lower than the 3.5 or so that I’d like to shoot for to be able to really call it a system.
If I add some basic versioning, and address some of the consistency issues that should put me squarely into a range that I consider to be a proper system.