Archive for the ‘Thoughts on development’ Category

Over the last several years I’ve written quite a few posts on DCI in various fora. Especially in response to someone trying to learn DCI. I’ve come upon the same problem over and over again and last night it happened once more. Essentially, it comes to this line of Q&A

Q: “I’ve started learning DCI and want to understand how it’s superior to OO could you please write an example so that I can understand”
A: “There are quite a few examples on http://fulloo.info/Examples. Have you studied those?”
Q: “Yes, but those didn’t compare OO and DCI, so I still don’t understand. Could you please write an example that will help me understand?”

The questioner is genuine in his wish to understand DCI but the line of questioning is in my view odd, but trying to tell anyone that, will usually end with an unfruitful discussion about the mentors way of teaching.

This post is me trying to shed some light on why this line of questioning doesn’t lead to the desired outcome for the questioner and to give an alternate approach.

Let’s try something else than a paradigm shift because they are inherently hard.  Let’s try comparing encryption schemes

Q: “I’ve started learning encryption and I’ve seen AES and DES. I don’t understand why AES is better than DES, I need an example that shows why”

A: “I could give you an example of a text encrypted with both but you want to be able to see why one is better than the result from the other. They are different in how they accomplish the result. If you understood some of the theory, such as entropy we could probably have an informed discussion”

Q: “I don’t want to have a theory discussion, I just need an example”

A: “There are plenty of examples out there, have you studied those?”

Q: “Yes, but they didn’t tell me why AES is better than DES”

A: “No and that’s because the difference is in the process if you studied some of the theory and experimented a bit we could have an informed discussion”

Q: “I don’t want to discuss the theory I want to understand it from looking at the result”

 

I don’t think anyone believes you would learn encryption from looking at the result. Even for extremely simple stuff. Stuff we can teach to small kids you can’t even as an adult learn from looking at the result. Below I’ve represented a concept you know, in an unfamiliar notation. It’s extremely simple and you even know the concept and theory, but I’m confident that you will have a pretty hard time answering the question

3 3 44

44 11 6

44 2 9

3 44 88

3 2 5

88 44 ?

 

 

It’s simple math. Stuff my daughter was able to do in her head before starting in school. But from looking at the examples you didn’t learn much. However, if you know just a little number theory and know the notation I’ve used, then it would be very easy for me to explain addition to you. Let’s repeat: This is a simple addition, none of the results are larger than 9 and you couldn’t understand it from five examples. However, if we had spent 25s for you to understand the notation and maybe 1 minute to understand numbers (if you didn’t already) then I could explain addition to you in 10s and you could use the examples to test your understanding. That is what examples are for. You test your understanding against them. Maybe your understanding matches and maybe it doesn’t. If it doesn’t you are likely to have a ton of informed questions that can help you further, but those are all questions you wouldn’t have been able to ask if you didn’t understand the basic theory.

Now imagine that we are not talking about the addition of one digit positive integer but an entire believe system.  First of all to discus which is more suitable we need to agree on what’s suitable. If we can agree on that we can then discuss which believe system is more stable. So let’s just say for now that we agree that comprehensibility makes a programming paradigm more suitable. Then we need to agree on what makes code comprehensible. There’s quite some research and theory into what makes something hard or difficult to reason about and theories can be found in  Neuro-/psychology, didactics. Philosophy and Neurology but also in math. The first four can tell us how we learn, remember and understand information, whereas the latter can tell us about abstraction and compression and other more abstract perspectives on information theory. If I can’t make you understand single digit math in five complete examples, then how am I going to show differences in comprehensibility based on theories in at least 5 different branches of science spanning both human and natural sciences, in one example that has to be small and therefor can have no chance of being complete?

 

I’m all for challenges, but this one is not one I think I’m going to solve presently. This is made even harder because most of the people coming to learn DCI has a preconceived idea that they know what OO is, however they usually know so technical merits that has nothing to do with the mainly psychological background and basis for OO. So they’ll first have to admit that what they thought they knew about OO is at best a half truth and sometimes even a lie.

 

If you have come this far you must be tenacious and I promise I’m almost done. I will wrap up with a step by step guide to understanding of DCI

  1. Accept that what you know about OO, probably has very little to do with the foundation of OO. After all the inventor of OO claims there’s only one real OO system in the world (the internet)
  2. Either familiarize yourself with research into how we learn, modeling and information theory or take the easy route and accept the postulates from authorities at face value
  3. Read articles about how to apply DCI, there’s a few on this site with likes to even more
  4. Experiment and don’t forget to reflect upon the result
  5. Ask questions based on your experiments and reflections
  6. Publish the resulting examples, at best as part of the Trygve repository
  7. Repeat step 2-5 until you gained the level of understanding you desire
Advertisement

Working on a project we’re trying out rest.li and we’re using windows as our OS that makes the installation a little more cumbersome and I’d like to be able to redo the installation at a future point so in any case I needed to document the process and hi, why not do that so some one else than me could benefit from it. I’ll also install HTTPie since testing the generated services is a lot easier with that tool.

So let’s get rolling

The first subtask will be to install HTTPie and this post from Scott Hanselman help me but things seems to have changed slightly since then, so I’ll include a revised version

Firstly you’ll need to download Python I chose the 64-bit MSI installer when it’s installed then add the installation directory to your path. To do so follow this process

  1. Right click “Computer” in file explorer
  2. Choose “properties”
  3. Choose “Advance system settings”
  4. Press the “Environment variables” button

Depending on whether you installed for all users or just you you should then change the corresponding path variable (either you or system) and add the installation directory and “installation directory\Scripts” to the path

The former makes python available on the command prompt and the latter makes pip (a package management tool we’ll be using later) available.

To test that you’ve succeeded open a command prompt (windows-r and type cmd) first type python and hit enter there should be no error telling you that python is an unknown command, then repeat with pip

Next step is to install curl make sure that the executable is in a directory that’s also in your path variable and test in the same manner as with python and pip.

Next step is to execute distribute_setup.py you can do this using curl that you just installed

curl http://python-distribute.org/distribute_setup.py | python
  • or if you have issues connecting using curl then download the file from the link above and simply double click it

Last step in installing HTTPie is to use pip

pip install -U https://github.com/jkbr/httpie/tarball/master

That will down load HTTPie and install it on your machine. Pip will place the httpie script in your python script folder so the tool is ready to be used from the command promt when it’s down downloading and you can test this by typing

http

at the command prompt and then hit enter.

We are going to need to install

  • A JDK (1.6+ as of this writing)
  • Gradle
  • Conscript
  • Giter8
  • rest.li

First make sure that you have the JDK installed. You can find the installer on Oracles site. Gradle on the other hand can be found at the Gradle download page. With both the JDK and Gradle installed we’ll install Conscript which you can find on the github page. It’s a jar file and it should be sufficient to download and double click it
once you have conscript installed you should add that to your path as well and test it by executing the command

cs

on the command line. With conscript installed it’s time to install giter8 to do so execute the following command

cs n8han/giter8

If you get an error saying jansi or some other lib could not be found. You might be blocked in a proxy due to the fact that your traffic can’t be inspected since sbt-launch will use a repository url starting with https. It might just work for you if you change it to http. You can do this (and will need to do it both for cs and g8) in the launchconfig file that can be found under your conscript installation folder in .conscript\n8han\conscript\cs for conscript and .conscript\n8han\giter8\g8 for giter8. If that does not work then you can try a slightly more complicated version which you can read about here

When g8 is installed and everything is working as it should. You can create your first project by simply executing

g8 linkedin/rest.li-skeleton in your command shell

.

We haven’t used gradle yet but you will have to when you wish to compile your rest.li project. The skeleton created in the previous step includes three gradle build files that will handle the code generation tasks for you. For how to actually work with rest.li and gradle see the rest.li site

Following a discussion on testing and architecture I thought I’d write a post. The statement was: When architecture is not informed by tests, a mess ensues. That’s of course nonsense. Versaille was never tested but is still recognized for it’s architecture.

The statement got me started. Rhetorically that’s a clever statement. It uses an old trick most salesmen has under their skin. The statement associates the item being sold (in this case that item is testing) with something with objective positive value. That would be information in this case. The statement is of course also logically incorrect but marketing never worries about mathematical correctness as long as the statement is either mathematically incomplete or ambiguous. However the statement was not taken from a marketing campaign but a discussion of engineering  practice between J “Cope” Coplien and Uncle Bob (the latter wrote it) and in that context it’s incorrect. The key is not the test but the information. How the information came to be is irrelevant to the value of the information.  So a more correct version of the statement would be “Without information mess would ensue” and that is of course correct but also tautological.

The real value of information is in it’s use. If you don’t use the information but disregard it all together it had no value. Since testing is retroactive you get the information after you are done with your work, you can’t improve anything retroactively. Unless of course you are the secret inventor of the time machine in which case retroactively becomes entirely fussy.

If you do not intent to create another version, the information you gained from testing has no value in respect to the architecture. So using test as a means to produce value in the context of architecture  requires an upfront commitment to produce at least one more version and take the cost of a potential re-implementation.  If your are not making this commitment the value you  gain from the information produced by your tests might be zero.

In short you have a cost to acquire some information, the value of which is potentially zero.

It’s time to revisit the statement that got it all stated and to try and formulate it somewhat more helpful than a tautology.

“If you do not invest in the acquisition of information your architecture will become messy” 

You can try and asses the cost of acquiring information using different approaches and the choose the one that yields the most valuable information the cheapest.

There are a lot of tools to use to acquire information. One such tool is prototyping (or even pretotyping). Prototyping is the act of building something you know doesn’t work and then build another version that does. In other words prototyping is when you commit to implement a version, learn from it by (user) testing and then build a new version. Might that be the best approach? at some stage for some projects, sure. Always? of course not. Prototyping and pretotyping are good for figuring out what you want to build. So if you do not know what you (or your business) wants then use the appropriate tool. In innovative shops pretotyping might be the way to go. When you have figured out what to build, then you need to figure out how to build it. The act of figuring out how to solve a concrete task is call analysis. Analysis is good at producing information of how to do something in the best possible way.

Bottom line: There’s no silver bullet, you will always have to think and choose the right tool for the job

Fine print: You can’t improve anything with testing. You can improve the next version with what you learn by testing but only if there’s a next version

Bootstrapping maroon

Posted: April 9, 2013 in DCI, Thoughts on development
Tags: , ,

At #wrocloverb in the begining of March I started to bootstrap Maroon and have now released a fully bootstrapped version. It took longer than I had anticipated based on the time it took me to write maroon in the first place. The main reason for this was that part of the process lead me to find bugs needing fixing or features required to implement maroon that maroon didn’t support(1). Both tasks some what mind boggling. How do you fix a bug in the tool you use to fix said bug? a lot of the time I ended up having two mental models of the same concept and to be honest that’s one more than my mind can handle.
One model was of what the current version was capable of and another was what it was supposed to do. All this was part of the motivation for bootstrapping in the first place and often the reason why a lot of compilers are bootstrapped too. The current state of the code is a mess. I knew it would be. I’m often asked questions like “Is the code then in fact a lot cleaner when using DCI?” and my answer is that it for me depends on how I design the system. If I design as I presented at wroc_love.rb 2013 (top-down) then yes the code is a lot cleaner, however if I design as I often do when not using DCI (bottom-up) then I often find that the code becomes more messy when I try to rewrite it using DCI and maroon was designed bottom-up.
I’ve done some cleaning up but postponed a large part of the cleaning up till the bootstrapping was actually complete so that I had a foundation to test my refactorings against. So the now live version of maroon is a fully bootstrapped version making maroon the most complete example of how to use maroon. The goal of the version thereafter is to clean the code up and make maroon a good DCI example as well. An example I hope to have both James and Trygve review if their time schedules permit. What bugs did I find?

  • Indexers on self didn’t work in role methods, so if an array played a  role you couldn’t do self[1] to get the second element
  • You couldn’t name a class method the same as an instance method. This wasn’t really a big deal because you couldn’t define class methods in the first place but since that’s one of the features added it did become an issue

What features were added?

  • You can define initialize for context classes
  • You can define role methods and interactions with the same name as methods defined for objects of the Context class. E.g. you can define a role method called ‘define’
  • You can define class methods (The syntax for that is still messy but you can do it)
  • You can use splat arguments in role methods or interactions. In general arguments for role methods are discouraged which I heavily violate in the current version of maroon
  • You can use block arguments (&b). The syntax for this is even worse than the syntax for class methods
  • You can choose whether to build the classes in memory or to output a classname.rb file for each generated class. That’s the way the bootstrapping works. I use the current stable version to generate the files of the next version. I then use those generated files to generate an additional set of generated files and the last set of files, which are generated using the upcoming version are then packaged as a gem and pushed.

What features would I still like to add?

  • Default values for arguments to role and interaction methods
  • Make it possible to use all valid method names for role and interaction methods E.g. field_name=, which are currently not possible
  • Cleaner syntax for both block arguments and for defining class methods. I have an idea for this but it won’t be until after I’m done with the cleaning up. It’s going to be an interesting task since it’s probably not going to be backwards compatible. The changes will be small and make the syntax intuitive to ruby programmers (which the current is not for the features in question) but since I’m bootstrapping I’m breaking my own build before I’m done.
  • a strict flag that would enforce a strict adherence to DCI guidelines and a warn flag that would warn if any of those guidelines were violated
  • Add a third option, that instead of creating classes in memory or class files will visualize the network of interconnected roles for each interaction.

(1) Yes it’s a bit strange to talk about bootstrapping. You think/say stuff like ” features required to implement maroon that maroon didn’t support” that sounds kind of nonsensical a lot. The most mind boggling part of that is when debugging…

What’s in a role

Posted: February 20, 2013 in DCI, Thoughts on development
Tags: ,

On a recent blog post I received a question “Is there hope for sharing roles between contexts?” and my answer to that might at first be discouraging. In short it’s “no”

I thought it worth while to explain this a bit more since it’s a question that keeps coming back. It has surfaced on object-composition several times, often though not always it’s been due to a misconception of what a role is.

How people perceive a role is to some extend influenced by their platform of choice. In Java, where  no one has yet found a way to do pure DCI, people often take a wrapper approach and this wrapper is then what is referred to as “the role” 1. In Ruby on the other hand people often confuse roles with modules because using extend to do injection based DCI is how you would implement the role logic (at least prior to maroon).

However the concept of a role is agnostic to implementation details. It’s first and foremost an identifier. At run time the role is what identifies the current role player. It also identifies the role specific behavior for that role player. or to use Trygve Reenskaugs words “A role is an abstraction that highlights an object’s identity and external properties while it ignores the object’s internal construction.”

Usually when people talk about sharing roles between contexts they talk about sharing behavior between contexts and not sharing roles. The latter is frivolous, it’s like asking if we can share the role of Claudius the uncle of Prince Hamlet in the play by Shakespeare and Claudius in the TV-series “I, Claudius“.

What then if we rephrase the question to “Can we share implementation of behavior between contexts?”  then the answer is “yes but…”. One important goal of architecture and design is to segregate based on the rate of change and based on reasons to change. Often it will be found that two roles do not share reasons for change, and that they in fact share behavior is incidental. Sharing implementation of behavior between roles in two contexts would in that case lead to accidental complexity.

In the Canonical DCI example there’s two account objects playing each their part in a transfer context and among other capabilities they both have a balance attribute.

Let’s say that in a different context in the bank, we need to calculate the balance of a given set of accounts. Upfront it would seem that a simplistic model of an Account might suffice.  We store the balance as a decimal number and add and subtract to this number as we withdraw and deposit. And initially this would indeed suffice.
However there’s different reasons to change at play. In the second context it’s the accounting department at play and when doing analyses for their Solcency II modelling, they realize that they need the ability to exclude certain kinds of events, before using them for machine based modelling and they need to get the balance at certain days in the past as well as projected into the future. Neither of which can be handled by the original model. Between these to views of an account there are several commonalities

The account context in the above example captures some of these commonalities. It’s a context that based on a collection of ledger entries can calculate the balance. It can be used in the case where you simply need the balance of the account, it can be used if you need the balance based on only a subset of the entries. They can be filtered based on e.g. date or type. The only aspect that it can’t handle is the projection but that is usually based on the account it self and therefor the account can play a role in a ProjectedAccount context. In this respect behavior is shared between different role players in different enactment of the same context and the behavior that’s not common, is placed in a different context, where a context is playing one of the roles.
I’ve seen numerous examples of where someone would want to share roles between contexts but all examples I’ve seen so far have fallen a part when analyzing rate of change for each or reasons for change to happen. There are plenty examples of “reimplementing” behavior because though at first it seems to be the same it’s not. Just take any mathematical operator. Conceptually there’s no difference between 1/3 and 1.0/3.0.
In a mathematical perspective they are semantically identical. In most programming languages they represent different behavior. Similar is often true for addition. E.g in C# 1+x and 1l+x (where the first is 32 bit integer addition and the latter is 64-bit addition) are semantically different. For the division of integers and floating point numbers there’s different reasons for change E.g. we could get a requirement for triple precision floating point or an arbitrary precision floating point (in C# that’s decimal) integers are them self with absolute precision but the operations have limited precision in their results, so a requirement to keep the precision in the result could arise and would require changing the type of the result to something different than the operands. (This of course would violate the semantics we’ve grown accustomed to).
If such simple behavior with very strict definitions can be semantically different and have different reasons to change, odds are that more complex business logic with loose definitions share the risk of ending up being different beast even though they at first seems to be not just similar but exactly the same thing. Be mindful of the importance of not to make two similar but different concepts into the same. Share behavior where the behavior is the same, “reimplement”  where it’s not. The dijkstra examples in Ruby are a good example of this, where several roles have the same implementation of role methods, eventhough it’s technically possible to share the implementation

1) This is the self schizophrenia showing it’s ugly head an perturbating the mental model of the developers, so that when they communicate they are use the same words as the users but a referring to slightly different aspects of the application

How Moby works

Posted: February 18, 2013 in DCI, Thoughts on development
Tags: , ,

As I wrote in my previous post I would write on how Moby maroon works internally. This is definitely not a post explaining DCI. As a matter of fact it might confuse you and potentially impair your understanding of DCI so tread carefully if you are new to DCI. The target audience is Rubyists that want to know how Moby maroon is capable of getting performance on par with regular method invocation and/or those that might want to contribute to Moby maroon and thereby indirectly to Marvin and DCI as well,

The context defined in the previous post is rewritten to an ordinary ruby class the source code of this class is returned as one of two objects from the define method. The first object being returned is the newly created class it self. The second object is the actual source of this class. In the case of the hello world example the source looks like this

class Greeter
   def greet
       p("#{greeting} #{self_who_say}!")
   end

   @who
   @greeting

   private
   def who;@who end
   def greeting;@greeting end
   def self_who_say 
     who
   end
end

As you can see there’s really nothing magically going on. So what did actually happen? Well firstly each role is rewritten to a definition of an instance variable and a getter. So for the who role there’s an instance variable ‘@who’ and a method ‘who’ and similar for the ‘greeting’ role. But there’s no role methods any more, where did the role method go? The last method in the source
def self_who_say
who
end

is where the role method went. The name might hint at this. Role methods are rewritten into instance methods of the context. And this is where you might be confused if you are new to DCI, so it’s worth stressing that this rewrite violates the mental model of DCI. Paradoxically DCI is very much about expressing the mental models and not violating them. A role method should be seen as a part of the role player and never be considered a part of the context.

it would have been safe to rewrite this to

def self_who_say
@who
end

So why isn’t it done? The reason to this is that there’s two rewriting phases in Moby maroon. The first rewrites the use of self to the corresponding role getter and the second rewriting phase depends on the role getter. When a role getter is encoutered the rewriter  will check to see it’s the instance expression of a role method invocation. If it is it will rewrite that invocation. E.g if we changed the who role to

role :who do
    say do
      self
    end
    talk do
      self.say
    end
  end

Then the first phase would rewrite talk to

talk do
who.say
end

And the second phase would the see this as a role method invocation and rewrite it to

def self_who_talk 
  self_who_say  
end

Recollecting from the previous post that if you call a method directly on the instance variable (in this case ‘@who’) it will always be an instance method and never a role method it should be obvious why the first phase rewrites to the role player getter and not the instance variable. Rewriting self to the instance variable would result in the second phase being unable to identify role method invocations.

Having seen how role method invocations are translated into invocations of instance method on the context obejct it should also be clear that there can be no performance difference between calling a role method or calling a regular instance method after all seen from the view of the interpreter role methods are instance methods.

In the next post we will look at how to bind block variables to roles followed by another post on how the rewriting handles this.

languages

Posted: December 6, 2009 in Thoughts on development
Tags:

working with c++ i often wander if c++ even qualify as a language. sometimes it feals more like a rather advanced compiler-compiler.

One definition could be unambigeous semantics. That would certainly disqualify c/c++. With the flexibility of say operator overloading comes the lack of semantics.

Think about it every statement involving any operator have arbitrary semantics. Take a look at the below example. it’s rather weird but none the less possible in c++.

C=Z;
assert_equal(C,Z);

A=B=C;
assert_notEqual(B,Z);
assert_notEqual(A,B);
assert_notEqual(C,Z);
assert_isNull(B);

and so forth. C might even be deleted and A uninitialized.

My point is that you can only have assumptions of what any given statement in c++ means. there’s no way of knowing what any piece of code Will do unless you know the entire code base. with that lack of semantics in even the most central statements such as assignment, I find it difficult to Call c++ a language.

I was reviewing code today and one of the things we discussed was if certain data should be parameters to a method or if they should be attributes of the class that held that method.

When I see a method I want every argument used in decisions or in expressions that will eventually make their way to the return value. If there’s no return value the method probably have side effects and if that’s the case I’d probably rewrite the method so for this post let’s stick to a method with return value.

The issue we had was that some of the arguments were actually used to ensure invariants but invariants not ever changing they are known at the time the object is created and should be enforced at the time of creation. If they are violated at the time of creation they are still violated at the time of execution.

The first point worth mentioning is to enforce constraints as early as possible to fail as early A’s possible. If some constraint can be enforced at compile time they should be.

The second point worth mentioning is that every argument given to a method should influence the flow of the method everytime and possibly the result as well. The only exception to influencing the result I can think of is when the result is partly based on things external to the method. E.g. if the method is returning calls to a database.

..and I’ll have you writing unit test until the next paradigm shift.

Today I was reading a some what oldish article on why not to use singletons. It had me laugh. I totally agree with a lot of his points and even if you do not agree it’s great fun.

It made me laugh even more because I’ve been on a project where I’m sure they at one point had a design meeting that went like this:

Manager: “We’re going to model the world”

Dev. Lead: “Cool and there’s only one world so we need a singleton for that”

Manager: “We need to be able to model Denmark, UK and USA”

Dev. Lead: “Ok there’s only one Denmark so we’ll need a Denmark singleton”

Manager: “We need to model Kastrup airport in Denmark, LAX in the states and Gatwick in UK”

Dev. Lead: “Well there’s only one of each of them so we’ll create a singleton for each”

Basically every single class in the design ended up being a singleton. the denmark had a method called getKastrup() which in turn returned the Kastrup Airport singleton and the US singleton had a similiar method called getLax().

My first line of questions went something like this:

Q:  “Why don’t you have a city base class with a getAirport method?”

A: “Well making a getKastrup() method on the US singleton returning the LAX singleton really doesn’t make any sense”

(Me think: “I absolutely agree but the US singleton it self doesn’t make any sense and you still have that one and others”)

Q: “Well what if all the countries was actually of the same type with a property holding the name?”

A: “No no we can’t do that our development manual states we should use singletons when ever we only need one instance of the object and we only need one of each of the UK, US and DK objects”

Q: “Well isn’t that 3 instances of the same type?”

A: “No not at all the US has a method called getLax(), the UK has a getGatwick() and the DK has a getKastrup() so if they were to be one type it would need 3 methods and for each instance only one of them would be valid, that would be bad design”

Q: “what about just having a method called getAirport() that could return the correct airport based on arguments passed on construction of the object?”

A: “Ah see that would be hard to read code, since there’s really no city called airport and the object we’re returning is named after the city, so you would expect to get an airport but either Lax. Kastrup or Gatwick and you can’t see that from the getAirport method name”

This went on and on. The really funny thing was for every decision they had an argument for having chosen as they had and almost every argument sounded right but made no sense what so ever.

I ended up having them write unit test for all there singleton based code, they had some test code already but that was mainly sunny day tests so I had them write code to actually find errors. It’s didn’t take them long to realize that singleton based code is not ment for unit testing. No need for arguments against singletons afterwards. Every single person that had had to write tests for singleton based code loathed them afterwards and all of them came with suggestions or requests for having the caode changed to some thing else.

As an a side I use strategy a lot, so reading the takes on strategy in the above mentioned article was just as fun. I totally agree it’s functional programing and I like it 🙂 but it’s very not OOP but a nice why of mixing the two.

I will be paying more attention to when I’m using that pattern in the future

Writing robust code

Posted: November 14, 2008 in Testing, Thoughts on development

A little more than a week ago I gave a speech on writing robust code to the dev’s of the team Im currently working for. I think I learned more from that speech than did the listeners.

Im used to highly object oriented people and hadn’t realized that most of the team have never developed OO style, so we had a lot of misunderstandings and strange looks but slowing we got closer and closer to the point I was trying to make and in the end we had a plan for the next session.

Having a second seesion gave me the option of integrating the knowledge I had gotten from the first session and rephrasing the goal in none OO terms, we still debated but ended up with a very easy to remember conclusion: “Only implement the needed functionnality”

That might seem very simple but take a look at you own code and see if you in any way can provoke the code to go down an unexpected path. If you have a switch modelling different states in your application with a very high certainty the answer is yes.

As one of the listernes worngly thought Im not advocating never to use switches, but I am advocating they make loosy state machines.

take the switch

switch(state)
   case StepOne:

  break;

  case StepTwo:

  break;

There’s absolutely nothing that enforces that stepone is handled before steptwo is even valid. If that’s the intention, fine no worries.

In our particular case we had a 4 case switch called 3 consecutive times giving a possible 64 paths through the code but only 2 of them was actually valid.

Changing the implementation from a swicth to an simple state machine reduced the possible paths through the system to 2. the state machine was implemented with a simple class
state
   state Next;
   IHandler handler;
   void Enter();
   void Leaver();

 IHandler
    void Execute();

That way it was very easy to link the states to give us the only two possible ways it was actually valid to travers our potentially 64 path execution graph.

The neat thing about the solution comes when you start testing your solution.
If you have an undetermined number of paths you’re at risk that one of the paths you hadn’t realized existed fails.

You can write code to handle those situations but if you forget to do so or just didn’t cover everything of what you didn’t know existed, it’s very unlikely you will spot it.

Wheras if you only implement what you need you will not have to worry about all those cases you dont even know about, instead if you mess up and forget something, you will find it in your test every time. You simply can test all the sunny day scenarios with out realizing that the functionality for one of those scenarios isn’t implemented.