The Physics of Software
an investigation into the true nature of software
  • The idea
  • The story so far
  • Papers
  • Random notes
  • Me

Friction, Potential Energy, and Power Law Distribution

1/28/2016

 
I’m writing these notes a few days before my talk “Software Design and the Physics of Software” at DDD EU (January 29th). The idea is to put them online the night before or the same morning.

In the talk I’ll be hinting at the connection between the notion of friction and the power law distribution in function size that has been observed in many projects. The intent of these notes is to:

- explain that connection 

- connect the dots with a previous note, on -ilities as potential

- put some science back in software design

Let’s start at the beginning. In my presentation, I describe the notion of friction in the artifact space, as a force resisting movement. So if you move code from the bulk (body) of a function into a new function, you need to do some work (partially hidden by refactoring tools if you use any) and part of that work is just friction, and goes in counterbalancing resisting forces (that I’ll describe in details in the talk). 

In a previous note, I’ve also proposed that many -ilities represent a potential; for instance, reusability is the potential to be reused. The notion of potential also applies in the physical world, of course. When you push an object up a slope, part of the work you do is lost (to friction) and part goes into potential energy.

It would be appropriate to conclude, therefore, that unless all the work we do to move code into a new function is wasted, code moved into a new function keeps some of that energy, in the form of a potential. In fact, once you move code into a new function, you now have the potential to call it from multiple places, a mild form of reusability, or to compose it with something else, etc.

Of course, at this stage we could still say that all this stuff is an interesting hypothesis and nothing more. So why don’t we use the method of science, instead of relying on our rhetorical skills? To quote the great Richard Feynman: 

"In general, we look for a new law by the following process. First, we guess it, no, don’t laugh, that’s really true. Then we compute the consequences of the guess, to see what, if this is right, if this law we guess is right, to see what it would imply and then we compare the computation results to nature, or we say compare to experiment or experience, compare it directly with observations to see if it works. 
If it disagrees with experiment, it’s wrong. In that simple statement is the key to science. It doesn’t make any difference how beautiful your guess is, it doesn’t matter how smart you are who made the guess, or what his name is… If it disagrees with experiment, it’s wrong. That’s all there is to it.”

So, let’s put some science in software design. We don’t see much, so this could be interesting. Suppose that the hypothesis above is true, that code separated into a new function has more energy than code merged into the body of another function. Then it would follow that adding code to an existing function (say, to handle a special condition or to do further work) takes less energy than creating a new function for that purpose (and having the existing function call into that).

Now, once again, we could just ask around, and collect opinions, possibly skewed by things like “in my preferred language / paradigm that does not happen because I want it to be perfect”. That’s ok if you’re looking for a sociology or even an ethnography of software (which is quite an interesting undertaking anyway) but I’m looking for a physics of software, so let’s push things a bit further.

If that were true, that adding code where we already have some is easier than creating a new function, assuming that on average people will take the path of least resistance (minimize energy / effort), that would be setting up a form of Yule-Simon process, or preferential attachment process, where things tend to go where they already are. Now, we know the consequences of that kind of process. We would end up with a power law distribution in function size.

So here is the thing: we need to check if that is true. If it is not, well, the theory above is wrong. If it turns out to be true, well, as usual in science it’s not a definitive proof that the theory is right, but:

- it adds some confidence in the theory, and we can keep it as a working hypothesis.

- indirectly, it also provides an explanation for the appearance of power laws.

Well, guess what, it turns out that we have a lot of literature exploring the fact that yes, on average, we have a power law distribution in function size. Just too much to add links here in a random note.

What is interesting about the reasoning above is that it provides an important insight: power laws appear when people operate under a more or less conscious minimization of effort. They do not need to appear, and have no magical power of self-regulating the size of your functions like some are proposing. In fact, in my Aspectroid Episode 1, a power law didn’t manifest itself, as I was aiming to contain every artifact to fit into a small page (and I did).

To repeat something I said in my presentation: more observation, less speculation. That would help a lot in software design :-).

Help me tune my DDDEU 2016 talk!

8/31/2015

 
I've been invited to present a talk on the Physics of Software at DDD Europe 2016. 

That caught me by surprise and I do not have any material ready for the talk, which could be a good thing (it's a chance to present fresh contents). I'm taking this opportunity to tune the talk with a small group of people interested in this line of work.

I've set up a trello board with the talk outline and I'll be recording the talk + slides in 10min installments (so let's say 4-5 for a standard 45min talk). More details in the trello board, but I'm interested in your feedback on both the outline and the contents. Both the board and the videos are private, because I like the idea that participants will see something new that hasn't circulated before.

Wanna help? Here is how:

- You need some familiarity with my work on the physics of software. 

- While I'm open to criticism on both the work itself and the presentation, I'm looking for feedback on the latter based on the premise that there is some value on the overall idea (otherwise, why do a presentation at all :-).

- You need a little time to look through the contents in trello, watch the videos, tell me what you think. Ideally, sharing feedback on trello will allow others to comment, but private feedback is fine anyway. If you have time to follow the discussion on trello and contribute, you're welcome. I don't expect this to turn into a high-traffic discussion anyway :-).

- It will take some time for me to complete the presentation, record vides, etc. So far I've recorded the most "technical" part, which is about 1/4 of the whole thing. So expect this process to extend for a couple of months.

- Ready? Get in touch via email or twitter to get an invite to the trello board (you will need a trello account, which is free of course). If we never had a chance to talk before (real life, twitter, blog, email, ...) a link to your twitter profile / blog / website / whatever would be highly appreciated.

Thanks for your help!


Non-functional properties and Activation Energy

11/30/2014

 
Many non-functional properties denote a potential. Reusability indicates a potential for reuse. That potential is fulfilled when we actually reuse an artifact. Information hiding indicates the potential to change a design decision without impacting other modules. It is fulfilled when you actually change that decision. Even readability indicates a potential. It is fulfilled when someone actually goes in and reads the code.

Suppose that you have some code in a stable (working) state X. However, that code is lacking some non-functional property, say extendibility in a specific area. From the perspective above, what you want is to move it into another stable (working) state Y with a higher potential. As usual, that won't come for free: you need to apply some energy (work) to change the state of your code.

Borrowing a page from chemistry, we can see a similarity with the notion of activation energy: "the minimum energy that must be input to a chemical system with potential reactants to cause a chemical reaction [...] Activation energy can be thought of as the height of the potential barrier (sometimes called the energy barrier) separating two minima of potential energy".

In a picture:
Picture
Here we have two stable states X and Y, which differ in potential by DH, but the activation energy, indicated there with Ea(X->Y), is higher than DH. 
The fact that activation energy (the energy you actually need to add to the system to move it from X to Y) is higher than the mere difference in potentials is commonly experienced in software design / development. Every time you see the possibility of a change that would increase some non-functional property, but decide not to pursue it because the effort may not pay off, you're experiencing a case where Ea >> DH. 

Of course, software is different from chemistry, because the dynamics of information are not exactly the same as the dynamics of matter. For instance, once you move to an higher potential state (say, reusability) you can benefit from that many times over, therefore balancing the initial investment. Still, a significant difference between Ea and DH may easily stop us from moving our code to Y, as Y is often just a potential. A lot of engineering common sense is merely pointing out these facts.

Languages and Tools

One of the niceties of writing software in the 21st century is that many languages come with decent development tools, which include more or less advanced support for automatic refactoring. Being able to select a portion of code and move into a new method, with all parameters figured out by a tool, or to automatically abstract some methods into an interface, etc., all lower the human effort (energy) required to move from state X to state Y. In chemistry, this is the role traditionally played by a catalyst; the purpose of a catalyst is exactly to lower the activation energy, as you see here:
Picture
Some language features act themselves as catalysts. Unlike many younger programmers, I learnt functional programming before object oriented programming. Lambdas and higher level functions were my bread and butter. In my first OO language (Smalltalk) I could still pass around code fragments with ease (as I always tell people, if is a method in Smalltalk, taking two code fragments as parameters). But when I moved to C++ and Java, I always missed lambda functions (until recently). 
Of course, there is nothing you can do with lambdas that you can't do with an interface, an implementation, and polymorphism. But the activation energy is quite high, and people tend to go with the "lower resistance path" (that is, stay with the lower potential, which in many cases means less information hiding here). In fact, the anonymous inner class idiom in Java was meant as a mild catalyst, but lambdas are way more effective as catalysts.

A dynamic language tends to lower the overall activation energy for most reactions as well. Which brings me to the conclusion for this short entry: although I have shown only one direction, reactions go both ways. You can move from a higher potential to a lower potential. You can mess up your code, so it still works (state X is stable) but it no longer possess some non-functional property you had in Y. 

A catalyst may lower activation energy both ways, from X to Y or from Y to X. It's easier to make a non-functional mess in a dynamic language, because implicit contracts make many non-functional properties hard to see. Lambdas, although extremely convenient, may prevent a bunch of functions to coalesce into new, meaningful abstractions (a richer interface), which is instead split into single pieces. Acknowledging this duality is part of a deeper understanding of things, and a good antidote to the superiority complex that comes with so many languages and paradigms.

Notes
[1] The pictures above have been adapted from a work in wikimedia commons, which license allows unrestricted use, including modifications.

[2] I wish I could say that I know how to put numbers on Ea and DH. I'm very far from there.

Shape (Form) as a Program

10/10/2014

 
An interesting intuition from D'Arcy Wentworth Thompson [On Growth and Form; I read the 1942 edition] is that the current shape of an organism or artifact is a byproduct and in a sense a mirror of the forces that were applied during its growth (or construction). Something is flat because it has been pressed into that shape, or cut into that shape. This may seem trivial, but allows for some interesting speculation about the original forces by looking at the current shape.

While thinking about software, I came to a larger conclusion, which may seem equally trivial, but that I feel may hold the key to a more precise formalization of many notions. The shape and material of an artifact are also a recipe for its future reactions to forces. A long beam will bend easily. A wooden table will scratch easily.

When you move into the realm of programming and artifacts, things get interesting, because a recipe is in itself a set of pre-encoded instructions. So, when you consider a software artifact (a class, a function, etc) you see that the artifacts encode:

- a set of explicit instructions for the "essential interpreter", the machine that will execute those instructions. This is what we normally call "the function" or "the behavior" of that code. Those instructions are explicit in the sense that they are directly expressed in the programming language; using poor old C, which is close to the machine, I can say:

int f( int x ) { return x + 1 ; } 

and that means "dear computer, please add 1 to this integer x and return the result".

- a set of implicit instructions for the "contingent interpreter" of change, encoded in the shape of code. This may sound like intellectual mumbo-jumbo, so let's ignore the essential / contingent terminology (which I've borrowed from Richard Gabriel [Form & Function in Software]) for a moment and focus on the notion instead. 
Just like a physical artifact pre-encodes a set of responses to forces, so does our software artifact. Those responses are pre-encoded in its form. That's a rather powerful notion. 
If you look at f, its pre-encoded response to change is rather simple:
  - if you want to use another type instead of int, you either change f (and lose the version working on int) or clone f / change the clone.
  - if you want to add another value instead of 1, you either change f or clone f / change the clone or add a parameter to f.
  - if you want a different algorithm you may just as well implement another function and avoid bothering about transforming f (e.g. to take another function as a parameter), as f is too simple to be worthy.

If I switch to C++, I can say

template< typename T > T g( T x ) { return x + 1 ; }

the pre-encoded reaction to change for g is slightly different, as it basically says:

- it's like f, but you can use it for any type for which "+ 1" makes sense at compile time. So if your change is from int to one of those types, the reaction is that no change to g is necessary (damping).

Of course I can also write:

template< typename T > T h( T x, T y ) { return x + y ; }

and this pre-encodes another slightly different reaction to changes. Or move to:

template< typename T1,  typename T2, typename T3> 
  T3 r( T1 x, T2 y ) { return x + y ; }

and that encodes yet another reaction to changes, even though they would all return the same value when used with an integer and (if necessary) a 1.

Now, if you review a number of programming / design notions from this perspective (OO polymorphism, genericity, etc), you'll see how shaping software in a specific way becomes less and less about "respecting some principle" and more and more about pre-encoding a reaction to change (in the decision space). That's basically another program, which might or might not be executed. 
Most likely, that program won't be executed by a computer: it will be executed by a human, the moment those changes will become necessary. But in a sense, that's completely irrelevant. When you code, you're giving a set of instructions to the essential intepreter (the computer) and a set of programs (*) to the contingent intepreter (the future bearer of change). The first is directly encoded in the instructions, the latter is indirectly encoded into the form (shape).

(*) it's a set of programs, not a set of instructions, because it's up to the contingent interpreter to choose the program among those pre-encoded in the shape of the artifact.
Forward>>
    Some random thoughts that
    I'm not smart enough to keep to myself.


    RSS Feed

    Archives

    September 2019
    February 2018
    July 2016
    June 2016
    May 2016
    April 2016
    February 2016
    January 2016
    August 2015
    November 2014
    October 2014

Powered by Create your own unique website with customizable templates.