Headers, babies, and bathwater

I claim that by eliminating the C/C++-style dichotomy between headers and implementation, most modern programming languages have thrown out the baby with the bathwater.

Don't throw out the baby with the bathwater! Photo credit: StubbyFingers (Flickr)

Don’t throw out the baby with the bathwater! Photo credit: StubbyFingers (Flickr)

If that sounds crazy, just hang with me for a minute.

I know my claim runs counter to popular wisdom; have a look at this thread on stackoverflow.com. Designers of languages like python and go and D and ruby and java consider it a feature that developers don’t have two redundant pictures of the same functionality. This comment from the C# 5.0 specification is typical:

“Because an assembly is a self-describing unit of functionality containing both code and metadata, there is no need for #include directives and header files in C#. The public types and members contained in a particular assembly are made available in a C# program simply by referencing that assembly when compiling the program” (p 3).

I agree.

Sort of…

Bad headers are a royal pain

It can be onerous to maintain the parallelism between a .h and a .cpp. And most C/C++ headers are managed so poorly that the benefits you might claim for them are theoretical rather than real. Three common antipatterns that I particularly detest: Continue reading

Features are not chunks of code

Before the industrial age, “features” were noteworthy aspects of a face or a geography: a patch of color, abundant wrinkles, a scar… The human brain is stunningly good at identifying and comparing such features–perhaps because that ability has been central to our nurture as children, our bonding into family units, and our survival as a species.

danie-franco-o1PKM7-8AH4-unsplash

Photo by Danie Franco on Unsplash

When we want to say what a face looks like, we describe its features. They are an entrée into experience with it.

At the dawn of the computer age, the advertising and publishing industries were already talking about how products–or aspects of them–could be “featured” in media. Highlighted characteristics were called “features”, and this metaphor transferred seamlessly into digital language. Software product managers now traffic in “features” and “feature sets.”

We use the term so comfortably that we sometimes forget what it has to teach us.

Continue reading

The 8th Characteristic

Biologists will tell you that life has 7 characteristics: organization, metabolism, irritability, reproduction, homeostasis, adaptation, and growth.

I think this list is missing something. It’s foundational, indisputable, and familiar even to kindergartners. But perhaps only kindergartners would call it out; several generations of biologists seem not to notice it enough to add it to their short list. Pick up a biology textbook, and you are unlikely to find a single chapter devoted to it.

Are you ready?

The 8th characteristic of life: mortality.

All living things die.

We see without seeing… Photo credit: John O’Neil (Wikimedia Commons)

Think about the consequences for a few moments. Would any of the ecosystems that you see on nature documentaries be possible without death? Would human civilization, as we know it? Read Orson Scott Card’s short story, “Mortal Gods,” and ponder.

Community blindness

Why am I writing about this as a software guy?

Continue reading

How to turn coding standards into epic fails — or not

Yes, a restaurant really displayed this sign. I doubt it influenced anybody’s behavior…

Some attempts to influence the behavior of other people succeed; others are doomed from the get-go.

Coding standards are usually written because we want to influence the structure or style of code produced by engineering teams. Sometimes they’re helpful; more often they’re ignored and forgotten; occasionally they provoke fireworks or bitter resentment.

I’m not sure there’s a guaranteed formula for success, but there’s a guaranteed formula for failure; let’s cover that first, and then see what helpful suggestions we can derive.

How to turn coding standards into epic fails

1. Micromanage.

Leave no room for personal style and creativity. Make no attempt to distinguish between meaty issues and utter trivialities. State all rules in absolutes; allow no exceptions. Announce enforcement in code reviews. Bonus points if you actually follow through on the threat, and double bonus points if you display some other developer’s code in front of the team as an example of egregious violations.

“Always put a space between an identifier and a curly brace, except in nested struct initializers where the first member is a string literal (other than NULL) or a #define’ed constant.”

“Begin every function with a comment that specifies the name of the coder, the date the function was last modified, the purpose of the function, an annotated history of how the function has evolved over time, a list of functions called by your function, your zodiac sign, and the names of all parameters. Make sure that parameters are listed alphabetically (case-insensitive), with a blank line between each, and the explanatory text after the param name indented 8 – (len(param name) mod 4) spaces.”

Continue reading

Decoupling Interfaces as Versions Evolve, Part 3

This is part 3 of a series. You can read part 1 and part 2 as well.

Quick Review

We want all the encapsulation and data hiding benefits that interfaces provide. We want to be able to version our interfaces so consumers can depend on them reliably, but we don’t want the producer and consumer of an interface to have to coordinate tightly. We don’t want the producer of an interface to have to version so often that there’s a built-in disincentive to follow best practice. And we want all the compiler and IDE benefits that early binding typically offers to a programmer.

I claim that no current solution really provides all of this — not COM, not SOAP-based web services, not late-bound REST web services.

Fear not.

Summary of Solution

  1. The provider of an interface and the consumer of an interface each conform to a compiler-enforceable contract (.wsdl/.idl/etc.), but unlike the traditional approach, these contracts are allowed to differ.
  2. The test of whether the two interfaces are compatible is not done by traditional casting, but by testing the contents of the two sides for semantic equivalence – a consumer has a compatible interface if it is a semantic subset of the provider’s.
  3. The consumer is required to write wrapper classes that forward from its own interface to that of the provider. (Using a language that supports reflection, like Java or C#, makes this task trivial).

Alternative Approach

Alternative Approach

The Gory Details

This solution could be built on top of COM, RPC-over-soap-style web services, or a RESTful service interface more analogous to document-oriented web services. Other environments such as CORBA/EJB may also be candidates, though I am less familiar with the details there.

Most SOAP comm pipelines get a remote object and deserialize it to a tightly bound object type in a single step, using a type cast as a runtime check that the remote source meets the calling code’s expectations. Such code would have to change so a remote object is fetched and deserialized in an initial step, and subsequently, the standard cast is replaced with a function that creates a wrapper object from the local interface if compatibility tests pass.

TryCast Pseudocode

TryCast Pseudocode

In COM code, the analogous initial step must return an IUnknown; the second step consists of composing the semantic union of all interfaces the IUnknown supports, and then using that überinterface as the basis for compatibility testing. Since IUnknown does not support enumeration, the semantic union of all interfaces in an IUnknown would require a list of possible IIDs to perform a series of QueryInterface calls, or a low-level analysis of the object’s vtable.

In a RESTful document-oriented web service, a URL returns an xml document that describes an arbitrary object using structural elements that do not vary across returned object type. For example, instead of

<book><title>Dragon’s Egg</title><author><fname>Stephen</fname><lname>King</lname></book>

you have

<doc><prop name=”title” type=”string”>Dragon’s Egg</prop><prop name=”author”>Stephen King</prop></doc>

or something similar. This conveys the object’s semantic constraints along with its data, much like sending a table definition along with a tuple in response to a DB query. The initial step of deserialization constructs a generic object; the second step tests compatibility against the semantic constraints embedded directly in the document and constructs an instance of a wrapper class on success.

It’s important to distinguish between read-only and read-write usage patterns in this mechanism. Consumers of an interface that only intend to display data are infinitely backward compatible if the runtime check for semantic compatibility passes, regardless of the version numbers/guids in play under a given scenario, because the wrapper classes depend on an interface mapping that’s generated dynamically at runtime. However, if a consumer of an object wants to update its state at the source, the wrapper class must contain every property that the provider will require – or else the provider must set such properties either before serving the object or when the update is requested. Using wrapper classes rather than the traditional generated SOAP stubs is an important element of this mechanism because this allows mods to objects that a client does not fully understand.

New Approach - Pros and Cons

New Approach - Pros and Cons