In a previous post I wrote about the realization that as of this year I'll have been programming for half my life, and wondering where I'm going to take my career from here. I left a few details for a later post: meta-thinking, and kindness in software. This post is about the latter. It's about kindness to users, as well as other developers.
I should be clear that this is a separate skill from personal kindness. On a Friday afternoon, looking forward to a weekend trip and working with a smile on my face, I can absent-mindedly create a tool which people will utterly hate using on Monday. Likewise, feeling disgruntled with the status quo I can be motivated to build something delightful.
The value of software for people who use it
I don't have too much to add here beyond the topic of agile software development. An awful lot has changed for the better over the years, and agile is a big part of that.
In summary, though, I'll say the following:
The waterfall model --- wherein I ask you what you want, go away and build it, and deliver you the result --- doesn't work well in today's faster-changing world (if indeed it ever did). For me the short summary of agile is that it's midway between the waterfall model, at one extreme, and Ray Bradbury's Jump off the cliff and build your wings on the way down at the other. Specifically:
* You don't really know what you want when we're writing requirements. But you will know what feels right when you use it with your hands, and you'll tell me about it.
* What you want will change as I'm building a solution for you, and the longer I go without talking to you, the wronger I'll get. So we'd better touch base often.
* Neither of us knows (unless I'm doing something very low-risk such as building a system nearly identical to one I've built before) everything I can actually do for you. So we'd better iterate back and forth, bouncing needs and solutions off each other in a very transparent way.
There are team-internal implementation details in agile, such as the scrum technique (daily 15-minute standing meetings to stay cohesive) --- but for developer/user interaction, one key point is user stories. It's useful for far more than software. If I'm doing something for you, I need to know: Why do you do what you do? What do you do? How do you currently do it? What do you hope for?
A quick sanity check I've used is:
* See if I can make a one-to-two-minute summary of what my customers' daily lives are like.
* If I find that I can't, then meet with them, take notes, draw pictures. Doodles help. Tell them what my summary is and ask them for their opinion on it.
* Remember that some quick notes are better than none at all: get at least an outline written down first. Make it prettier as I go on, if that turns out to be important.
The other key point for agile, besides user stores, is the sprint cycle, typically two weeks, which is the longest amount of time user and developer work independently. It formalizes the interpersonal feedback loop and makes it part of a scheduled process.
I've recently used these techniques on the job as a developer, and I've benefitted as a user from software built using agile. I have a lot more to learn about it, but I already know it's a better way to live.
The value of software for people who build it
Programming is the art of telling another human being what one wants the computer to do. -- Donald Knuth
Programs have two kinds of value: what they can do for you today, and what they can do for you tomorrow. -- Martin Fowler, Kent Beck, and John Brant, in Refactoring: Improving the Design of Existing Code
Code will be read and modified more times than it will be written. -- Andrew Hunt and David Thomas, in The Pragmatic Programmer
It’s harder to read code than to write it. -- Joel Spolsky's Fundamental Law of Programming
If your users are happy, is that all that matters? More and more of our world involves software. Moreover, as systems get bigger, open-source libraries continue to amaze, and we all can finally do more reusing than reinventing the wheel ... we're spending more and more time working with each other's stuff.
We had better be nice to each other.
Writing to be read
It's tempting to think that code only matters to the compiler and the platform executing it: that code is for the machine, that all we have to do is get the program working and move on. But in today's world, CPU, RAM, and disk are all cheaper than ever. Meanwhile our time and our happiness are as valuable as ever. I believe we should design with concern for the resources which are truly scarce. This is not a zero-sum equation: coding for people is not mutually exclusive to coding for performance. In fact, I claim that it's easier to optimize a program you understand than one you don't --- likewise for a program which has been designed to be modified.
So where do we start? The thing I hate about Spolsky's Law is that it's true. It takes time and effort to up-end it, every time, to turn a confusing program into a joy to read. The good news is that a few small steps can make a big difference.
We spend a lot of time on things like "Where is the code that does task X? I'm looking at code C ... What does it do? How does it work? Who uses it? Who will be affected by my change to it?" Time goes by --- maybe a lot of time. I think the best thing to do for readability is to tackle each of those questions head-on:
* Every significant project should have some kind of wiki page which makes a mental map for its source files.
* Source files should contain a simple hyperlink back to their wiki pages.
* Code can point the reader not only how it does what it does, but why it exists and who uses it.
* If I think I'm writing general-purpose code and I don't know to how many uses it will be put, that's great -- but I can at least list out the nominal client(s) as of when I wrote it.
Good enough for tomorrow
In a previous post I wrote about (among other things) technical surplus and multiplicativity. This is a specific instance of both. A trap too easy to fall into is to build things which are good enough for today --- and to tell ourselves that anything more is a waste of effort, or a waste of the customer's money.
In between perfection (unobtainable, and harmful to aspire to) and good enough for today (which leads to rigid legacy code that holds us back) is good enough for tomorrow.
Working today, we don't know exactly what will happen next ...
... and so we might think it's a waste of time to even try to guess ...
... but we can think about things likely to happen. In particular, if we create a useful data stream, a reasonable next step for someone to take is periodic reporting of that data stream. Or integration into a new display environment. Or porting it over to another, yet similar, project or line of business.
In summary, in addition to designing for present-day function and performance, I like to also:
* design for understanding;
* design for verifiability;
* design for re-use.
In summary, in addition to designing for present-day function and performance, I like to also:
* design for understanding;
* design for verifiability;
* design for re-use.
A question I can ask myself: am I building exactly a solution to the current problem, or thinking ahead by splitting out clear (preferably standards-based) modularities between reusable components?
A very, very common pattern I see is programs mixing computation and display in the same routine. These programs work. But they're hard to change. By separating the computation and display routines, we make it easier to re-task the computation for command-line, report, and dashboard environments.
Another question I can ask myself: looking at this current solution I'm creating, what's likely to be the effort to (a) bug-fix it? (b) add features? (c) integrate it into a larger flow? (d) adapt it to other, similar uses? I can strike a balance between spending too little time (creating technical debt) and spending too much (which is time-wasting perfectionism) by the following: envision reasonable scenarios for the future of the software, then simply keep the extra time I spend now less than the total future time cost to others that would otherwise occur.