The latest expert opinions, articles, and guides for the Java professional.
I’ve been trying to write code in a more functional way lately. I haven’t fully bought into functional programming yet, but it has more and more influence on my code. And yet, not long that ago I was fully convinced–like many programmers today–that the object-oriented way of doing things was the right way.
I designed class hierarchies that had mostly mutable properties–this kind of code seems to follow naturally from design-thinking that starts with object hierarchies or class diagrams. I’m now wondering if that was just an artifact of a computer science education that put an (over-)emphasis on object-oriented design and related subjects.
Functional programming doesn’t mean anything else than programming with pure functions (in the mathematical sense). However, this simple statement has wide ramifications when you think about whole programs. Ideally, a program is also nothing more than a pure function, composed of smaller functions. And most programmers (including me) don’t really know how to design complete programs this way.
The software industry’s focus on object-orientation has influenced me and others to evaluate code mostly according to object-oriented design principles. Coding in a more functional style makes me reconsider whether the quality judgments based on that paradigm are really the right ones.
I’m trying to figure this out at the moment. I think it’s good to start with outlining a framework for evaluating code quality, before getting into the different effects that both object-oriented and functional paradigms have on code quality in future posts.
Qualities of Code
What kind of qualities are we looking for in code, anyway? While there’s always room for different opinions, I think some common broad qualities exist. Here’s the TL;DR version:
- Fitness – code is focused on solving a problem that needs to be solved
- Correctness – code does what is specified, with no bugs
- Clarity – code says what it does without fluff
- Performance – code runs efficiently
- Maintainability – code is easy to modify by the next programmer(s)
- Beauty – the code is pleasant to read and meets certain style guides
Fitness and correctness are the most important–especially the aspects of these qualities that are user-facing. They tell us whether the code works, does what is needed and does it correctly. If the code doesn’t work or doesn’t do what is needed, then all the other qualities are irrelevant. Clarity is somewhat subjective; performance is objectively measurable, but usually a separate concern; maintainability is more of a social problem and beauty is mostly derived from the other qualities.
And here’s the long version which explains in more detail what I mean by each of these qualities.
Fitness of code (program, library and/or module) tells us whether it serves a useful purpose and is focused on doing one thing well. The focus is specified either in explicit documents or implicitly.
How can we verify that the code is fit? I believe only users can make that judgment; whether they are programmers coding against a library or end users of a program. The author can only do their best to design the specification well, and that could be art, science or craft.
Note that fitness doesn’t always mean the code should be lean. Some code may be full of corner cases out of necessity, or focused on building a long-lasting platform that puts backwards compatibility above all else. Such code might look uglier than it needs to be at first glance, but could be very fit for its intended purpose. Fitness is always relative to the external needs.
Correctness says whether the code does what is specified, hopefully without bugs. The main ways of verifying correctness are tests and types checked by a compiler (with static analysis tools falling somewhere between these two). According to the Curry-Howard isomorphism, types can be viewed as proofs. By using more sophisticated types, we can prove more sophisticated properties about code.
Types are compulsory if you use a statically and strongly typed language (which you should)–-you can’t run the code without compiling and type-checking it. But you still choose whether to work symbiotically with the compiler or work against it (e.g. by writing stringly typed code).
Tests are verified by automatic test suites or manual testing, which usually happens less often and might be skipped entirely. But tests can still prove some properties (or lack of unwanted properties) for which the compiler is not advanced enough.
I strongly believe that whenever we can, we want to prove properties about code as early as possible–at compile time rather than the uncertain time of running the test suite.
Clarity of code tells us whether we can infer the specification from the code. Separate documentation gets out of sync, so ideally we should be able to read the code itself to find out what it is supposed to do. Code is also written for humans to read, not only for computers to execute.
Clarity concerns readability, intentionality etc. Some of these qualities are vague–what is readability? I have written about that before, but I can’t even attempt to define it universally. I think it is different for each person, and hard to judge objectively.
Intentionality is perhaps more easily judged: usually it is possible to identify how much of the code says something about the specification and how much is boilerplate. Avoiding boilerplate and being concise helps, but there are different kinds of conciseness.
Ideally code would be it’s own spec, so that the two can’t get out of sync. I don’t know if such perfection is ever achieved, but it’s worth striving for.
Performance is judged by measuring it. You can optimize code to run fast or to use little memory, but usually not both at the same time. The ideal code is a tradeoff that perfectly matches the specification.
So if performance is not specified (even implicitly), who cares? I find that usually there is at least an implicit specification: users should not think that the code is taking longer than necessary to run, or hog more resources than necessary.
Maintainability is the ease of keeping the code and its spec up to date with changes in the world or in the goals the code is meant to fulfill. It is more of a social problem than an engineering one. If the code looks perfect to you and your employer can hire people who think the same way, then that code is perfectly maintainable!
There may be some less subjective things to consider here, and I think it mostly follows from the above: if the code is fit, correct and clear, then it should be easy to maintain. If it does not adhere to the fitness and clarity principles like Keep It Simple Stupid, You Ain’t Gonna Need It and Don’t Repeat Yourself, the maintainability goes down.
Beauty is the most subjective quality. I used to think this was mostly about how the code looked and whether it used good names, but the beauty of code can be seen from very different angles. If we pretend to have a common notion of code beauty, I think it’s twofold.
Firstly, beautiful code has qualities from the preceding measures–fitness, clarity etc. Form follows function. Having a style guideline also helps, as screwing up the formatting will make code seem sloppy despite of any of the underlying qualities.
Secondly, code that contains some clever hack or innovation that is in itself interesting for entertainment or educational purposes. If it is a really clever hack, it probably has some of the above qualities as well.
Summary and Further Discussion
The content above is only a rough outline for assessing code quality. I’d really appreciate it if you would let me know if you think there’s a fundamental quality that I left out, or if you think I’m plain wrong about something.
The primary concerns are those that users have to deal with: fitness and correctness tell us whether the code does what is needed or expected, and performance whether it’s done with reasonable use of computing resources. If the user-facing qualities are not there, then who (besides the author) cares about the specifics of the code itself? Clarity, maintainability and beauty are only the programmers’ concern.
To get back to functional vs. object-oriented programming, I think we can group the 6 qualities by how they relate to programming paradigms:
- Fitness, correctness and clarity may be most effected by design & paradigm
- Performance is usually a separate concern, often the subject of tuning
- Maintainability and beauty usually automatically follow from the above
Functional programming might be more concerned with fitness, correctness and clarity than object-oriented programming. I think OO design is mostly concerned with fitness, but doesn’t have much to say about the other qualities. More about this difference in my future posts on this topic. Please share this, leave comments below and reach out to me on Twitter at @t4ffer.
Leave a comment