Why we need try…finally, not just RAII

The claim has been made that because C++ supports RAII (resource acquisition is initialization), it doesn’t need try…finally. I think this is wrong. There is at least one use case for try…finally that’s very awkward to model with RAII, and one that’s so ugly it ought to be outlawed.

The awkward use case

What if what you want to do during a …finally block has nothing to do with freeing resources? For example, suppose you’re writing the next version of Guitar Hero, and you want to guarantee that when your avatar leaves the stage, the last thing she does is take a bow–even if the player interrupts the performance or an error occurs.

…finally, take a bow. Photo credit: gavinzac (Flickr)

Of course you can ensure this behavior with an RAII pattern, but it’s silly and artificial. Which of the following two snippets is cleaner and better expresses intent? Continue reading

When good comments mean bad language

I’ve had an epiphany.

For years, I’ve urged developers to write better comments. I still claim that’s a good idea (a very good one), but as I’ve pondered what a better programming language might look like, I’ve come to an important conclusion:

A lot of “best practice” commenting is just workarounds for inadequate language design.

This might seem like a crazy or arrogant claim. The Wirths and Matsumotos and Hejlsbergs and van Rossums and McCarthys of the world are incredibly smart people; how could I claim to know something that they do not? Each of these language designers has probably forgotten more about computer science than I will ever learn.

And yet, I think Randall Munroe (the cartoonist at xkcd) was right to make fun of our industry’s facile assumption that context-free grammar is all you need to know about formal language:

image credit: xkcd.com

To show you what I mean, I’ve inlined snippets of code from a variety of programming languages below. Don’t worry about digesting them carefully right now, but give them a quick glance and then move on to my analysis, and see if you agree with my claim about an unhealthy pattern. Continue reading

Good fences make good neighbors

In Robert Frost’s poem, “Mending Wall”, two farmers meet each spring to rebuild the rock wall between their properties. One farmer is the narrator. He notes that the unseen forces of winter and weather always cause some decay (“something there is that doesn’t love a wall”), and he wonders why the wall is necessary. There’s apple orchard on one side, and pine forest on the other–it’s not as if something will be kept in or out. The other farmer answers with the repeated aphorism “good fences make good neighbors.”

photo credit: DragonWoman (Flickr)

This poem could be a treatise for the principle of encapsulation in software. In software as in life:

  • Something there is that doesn’t love a wall.
  • Good fences make good neighbors.

What doesn’t love a wall?

Subroutines, formal interfaces, data hiding, class hierarchies, the pimpl idiom, and similar mechanisms all create barriers in software between consumers and providers of functionality. These techniques are well known, but we still have codebases littered with protected data members, unnecessary class declarations in headers, goto, and other suboptimal choices.

Why? Continue reading

Programming Language Popularity Index

Here’s an interesting chart, giving a realtime view of which programming languages have high mindshare. The chart has one axis devoted to number of lines in code commits on GitHub, and another to how often the language shows up in tags on StackOverflow.

langpop

Programming languages: what’s hot (top right), what’s not (bottom left). Top 3 rows of buttons are clearly where mindshare is at in the industry. Click for details.

I don’t think the chart is perfect. I’ve seen it billed as a “popularity index,” but I think it might be better described as a measure of how busy the coders are who use each language. If most of the coders who use a language hate it, I don’t think it’s fair to call it “popular.” Some apples-to-apples issues are glossed over, such as the fact that certain languages are very verbose, and some languages tend to get used mostly for “big” projects or for “small” ones. And the chart says nothing about the quality of systems built with the languages, or about the velocity of teams.

Continue reading

Small Files Are Your Friends

Yesterday I was discussing refactoring priorities with a colleague who’s a brilliant engineer, and I happened to mention my strong desire for smaller files in our codebase. I told him that I thought .h and .cpp (or .py or .java or .whatever) files with thousands of lines were a problem.

He asked me why.

He told me that he wasn’t opposed to the idea, but he always felt like it was more of a stylistic choice than a true imperative for good code. And he was curious to see if I could convince him differently.

After I pondered his question for a while, I realized that some of my opinion really is traceable to prejudice. I usually use IDEs instead of vim/emacs, and I think that promotes click-back-and-forth-and-hyperlink-in-many-little-files instead of open-a-big-file-and-scroll. My compatriots that are more console-centric are just as smart and effective–maybe more. So I’ll write that part off.

However, I also found some arguments for the small-file principle that feel more substantive. Small files are your friends.

More small friends. Photo credit: miguelandresen (Flickr)

Named scopes and cognitive complexity

The case for small functions is more discussed than the case for small files, and it has been made by almost every luminary in computer science. My colleague immediately conceded it, and I won’t repeat it here–but I will claim that many of the same arguments apply to files as well, because files as well as functions are an important named scope in software development. This in turn suggests some constraints on files with respect to cognitive complexity.

Studies of memory and human attention consistently demonstrate that we think best about small sets. This fact is reflected by the amount of detail visible within any given named scope, both in programming and in other thought tasks. How many top-level menus in the average application? Colors in most cultures’ divisions of the rainbow? Parameters in an easy-to-understand function? Sections in the average book store? Steps in easy-to-follow driving directions? (There’s a whole field called cognitive ergonomics that explores why these questions always have similar answers.)

How many functions should we put in a reasonable file?

For me, 2 or 5 or 10 feels tractable. 50 feels excessive.

If a “good function” also respects the cognitive complexity constraints of the human brain–not being too big to read in a screen or two, for example–then you end up with a reasonable upper boundary on file sizes of, maybe, 500 or 1000 lines. (See Steve Yegge’s insightful rant about code size being an engineer’s worst enemy. He focuses on codebase size, but much of what he says applies just as well at the next level down.)

I suppose that this argument is weakened by the features of some IDEs, which collapse tangential code blocks, display treeviews of functions, and support lots of hypertext-style navigation. But not all programmers use the same IDEs, and not all interactions with code are IDE-driven; file size remains relevant. There’s a reason why C# created partial classes to improve on java’s lump-it-all-in-a-single-file constraint…

When humans try to remember more than their brains can fit, stuff falls out. Big files mean that coders have to mentally model relationships between stuff that’s separated by way too much screen real estate. This is a recipe for bugs. It is also a serious impediment to learnability.

Loose coupling and encapsulation

Files are a natural unit of coupling. In most programming languages, you can declare a construct (a variable, an internal function, or class) within a file, and have that construct be invisible to the outside world. This means there is a built-in temptation for functions and classes to bind more tightly when they’re in the same file, because they have access to common but private knowledge. By breaking large files apart, you remove the temptation, break unnecessary dependencies, and promote looser coupling.

Another way to say this is that file boundaries are an encapsulation barrier. Use them to hide data. (See my recent post about encapsulation as a simplicity strategy.)

Code reuse and testability

A consequence of files hiding data is that when you have a function that might be useful in a dozen different modules, but the function is buried in a large file with lots of dependencies extraneous to that function, reuse and testability are both frustrated. If the function is in a file of its own, it’s more discoverable, and it’s reusable and testable without extra baggage.

Link optimization

A C/C++ corollary to the file boundary issue has to do with linkers and binary sizes. In many cases, linkers remove unused functions at compilation unit level, rather than at the individual function level. A .c or .cpp file is either in or out, as a unit. This means that if you have a .cpp file with 50 functions in it, and you call only 1 of them, all 50 get linked into the final binary. The result is bloated binaries. So: smaller .cpp files ==> smaller binaries. (Before you flame me about linker optimizations, I will admit that some linkers get more granular, depending on which switches you use. But it’s surprising how hard it is to do better than what I’ve described. Experiment and comment with your results.)

Counter Argument

I suppose you could argue that by making lots of small files, you’re creating more complexity in directories, in makefiles or projects, and so forth. Is 250 files in a folder worse than 15? Doesn’t that violate the “cognitive complexity” guideline above?

My comeback is: use packages or subdirectories or libraries (another level of management). You can’t subdivide forever, but you don’t need to.

The bottom line for me is experiential, not theoretical. I nearly always have cruddy experiences in code bases where large files are common. Small files don’t guarantee pleasant and productive work, but big ones seem to go hand-in-hand with other problems. I find it telling that codebases with big files are also codebases where people lament the lack of comments the most, for example. Over the years, I’ve become convinced that a simple rule of thumb about keeping files small will pay off more handsomely than almost any other coding best practice.

Action Item

Leave a comment to tell me what you think. Am I making a mountain out of a molehill? Or do you feel strongly about small file sizes as well? Have I omitted any important pros and cons from the discussion?