home | sought

Monotonicity in Practice: Onwards and Upwards

I met a Zen Buddhist on a flight to London a few weeks ago. British Airway's business class, the seats face each other, so you sit down for a 10 hour flight, hello, hello. We ended up chatting for 5 hours. The thing that stuck out to me the most were his descriptions of what Buddhism was. I'm used to the koans, and the idea that there are parts of the philosophy which cannot be explained. They must be intuited. Absorbed by osmosis. Puzzled out. Though, he had lived in a monastery which taught without parables, and perhaps because of this, his explanations were some of the clearest I've heard. One of the insights, people tend to shrink themselves onto something. To align themselves psychologically with some external concept and estimate their value based on it. For instance, a rich man shrinks themselves onto the identity "rich" and bad luck fills him with sorrow. Same for the careerist, the singer, and the overly-obsessed. When that estimation of themselves is redrawn, they are left without footing, but you don't need to shrink yourselves onto something. You can understand your independence from it. Avoid suffering from the wrongful contraction of identity onto something which we are not. Avoid a step in the wrong direction.

Monotonicity is the property of something to only move in one direction. A function whose derivative is always greater than or equal to zero. Something which only goes up. Something which only goes down. We can extend this idea to multi-dimensional spaces in a few ways: either dimension-wise monotonicity or the monotonicity of an image from the multi-dimensional space. In reality, the world is so complex, we can't guarantee the monotonicity of a system. Though, in design, either given certain constraints, we can use the idea of monotonicity as a narrative towards a particular goal to analyze feedback mechanisms and incentives.

Now, in general, the monotonicity we can achieve is approximate. We must give up something to get something else. We must take a step back to take a step forward. We must remove ourselves from the object we have shrunken onto. In some cases, the problem domain may be so difficult, we cannot find a smooth gradient towards the goal. Though, if a problem is such that we can find a reward function which has a continuous gradient towards the global maximum (or a good enough local maximum), then we should take that monotonic solution. If we take all the problems we have, and we can establish monotonicity in the ones which are simple enough, then we can free up our mental resources for the more difficult problems.

The closer we are to a true mathematical system, the more finely we can realize monotonicity. A codebase is about as mathematical of a thing that we operate on as human beings. Now, there are several hard problems here outside of this domain, including how to decide what changes to make. Though, assume we have a change we want to make. Once you have this goal, if the codebase is small enough or if you are logistically advanced enough, you can apply it atomically throughout the codebase. However, when the codebase size is large and there are many developers on it (millions, billions of lines, 100s, 1,000s of developers) or there are external constraints, it becomes impossible to apply the change at once.

For an example, imagine a codebase is using an XML parser which is no longer supported, and you need to migrate to a new one. The hard problems here are: Why are we using XML? Could we maintain our XML parser instead? Though, once you've decided to migrate to the new XML parser, the question is, how to do it? You can start moving code to using it, but in a large enough codebase, other engineers will modify parsers that you have started migrating. They will add new parsers. Sometimes, the amount of work remaining can grow faster than you can fix it. So, you need to redesign the system in order to make sure that you converge on a completed migration. Trying to do the migration naively results in more work overall. If a migration isn't done monotonically, it may meander for years. By always moving towards the goal, we ensure that as long as enough work is done, we will get there. No one has time to go around and manage everyone in the codebase, making sure they are doing the right thing. The codebase should be programmed to move itself towards deliberate goals.

In the example of the XML parser, we don't want people to introduce new code which runs against the old parser. We need some mechanism programmed in which ensures that if there is a new piece of code parsing XML, it uses the new parser. One way to do this is to keep a list of all the old exceptions (all the places where the old parser is used). If the dependency to the old parser is included in a new place, consider this to be an error. Then, make sure you only need to fix things once. When an old parser is fixed up, remove this from the exception list. Exception lists can be done in any way: by project name, by directory, by any metric which can delineate the clean from the unclean. Then, you can be sure the work that you do in order to make the migration happens won't go to waste. In some cases, you can even use the codebase to communicate to other devs, when they are working on existing part of the codebases, how they can incrementally migrate what they touch.

When we move further from mathematical objects, our ability to make an exact monotonic function diminishes, but we can still think of things in terms of monotonicity:

Now, this is where it gets tricky. Saying something is monotonic or not actually encodes a value system, and whether a value system is relevant is a hard problem. Which value system to choose (emptying the dishwasher to make room for dishes from the table VS meeting someone important to you on time) is an extremely hard problem. The narrative of monotonicity is a framework towards a specific goal, but it doesn't tell us which goals to make. Further, given any goal, we can construct a narrative of monotonicity that includes steps back. If you view a step back as a necessary learning experience, then it can be viewed as progress towards the stated goal. This is a case where a posteriori explanations can become problematic. We can always justify a step towards a goal as necessary. Though, that doesn't mean it's the right choice for everyone or that it should be repeated. There is a tempting fallacy fallacy here too. Mistakes can be learned from; they don't mean that we abandon a goal. So, we should avoid conflating "this is still on my abstract path towards a goal" with "this is what I wish I would have done." If we are redesigning a program/process/system, the question is: how can we know an easy path to a goal, and how can we define that in advance. The more complicated the explanation, the more corners we need to think around, the more abstract our sense of monotonicity is.

Then, the question is, why even talk about monotonicity, if we can construct a version of it which includes arbitrary actions towards any goal? Now, monotonicity is not something intrinsically good or bad (it carries no connotation). It's notable because it's simple. By moving only in one direction, we don't need to weigh what pushes up from down, what pushes expensive from cheap (if we do need to encode these things, it's in the abstract function we use to achieve a discussion of monotonicity). Though often not by name, working towards things in a monotonic fashion is natural. Regulators do this to phase in new standards in new products while leaving existing ones untouched. Contributions to a charity are typically monotonic: you can't take the money back. People board planes monotonically. In many of these cases, monotonicity is intrinsic or "obvious" to the problem domain. The reason to think about monotonicity is to try to make something as complicated as migrating components in a software system as easy as boarding a plane. If you are ever to reach some goal, you must have a monotonic narrative towards it in some abstract space, so find that space and suggest a narrative in it.

A final warning, the danger of monotonicity. In a changing world, once we are moving towards a goal (monotonically or not), we must also be aware of, say, the due-course hazard. When we are stuck on a course, the easiest thing is to stick with it. When we build a system to reinforce itself towards a goal, it becomes even harder to change it. Monotonicity isn't the only thing we should be paying attention to. We should also be paying attention to things like the rate we are moving towards a goal, the number of changes we are making simultaneously, the ability of people to understand a changing system, a new understanding of what goals we should be after, opportunities that arise, changes in resources, or any number of other factors. So, I propose monotonicity as an explicit narrative structure to tie aspects of a process to a resolution. The more formally this story can be weaved, the greater an assurance it is that we are reliably moving towards a goal.

When that goal is living a happy life, as the Buddhist told me, don't shrink yourself onto something. It's setting yourself up for setting yourself back. Onwards and upwards!