On Wednesday, November 20, I will give a lecture on software assessment to second year students from the University of Bern. The lecture is part of the introductory course to software engineering, and it comes right after the software quality one.
Software quality tackles things from general to the specific by instilling rules that might have an impact on future development. Software assessment goes the other way around and offers skills and tools that help developers tackle specific problems. Interesting enough, the easier software assessment is, the more quality there is.
Assessment is tough to teach outside of a real context. The problem with context is that you have to be in it to relate to it. Talking about it in general can be entertaining, but it has less impact.
The setup of the course happens to require the students to work in teams and to develop actual projects over the course of the semester. In this particular case, they have to build Android apps. To make them relate to the idea of crafting custom analysis, I will use their own systems to exemplify assessment scenarios.
In the meantime, here is a teaser picture showing the system attraction view for each of the 9 systems. The grouping shows the systems that tackled the same topic. The visualizations reveals that even when tackling similar requirements and even in a short amount of time (a handful of weeks) distinct teams will produce radically different structures. Software development is a complex game and we have to approach it accordingly.
The development of Moose 5.0 has recently moved to Pharo 3.0. The process of migrating from Pharo 2.0 was smooth, the only thing left is to integrate the GTInspector.
This turns out to be a tricky endeavor. The problem is that the SpecDebugger from Pharo relies on the inspector being implemented in Spec as well. Given that GTInspector is implemented in Glamour, this leads to collisions. As a consequence, installing the GTInspector leads to errors in the debugger. The problem is particularly hard because the error actually happens during the opening of the debugger.
When this happens, Pharo provides an emergency printout of the problem, but without any interaction possibilities. Essentially, you end up with an environment that is no better than a limited console.
After a first round of fixes, we ended up on the problem of
UndefinedObject not understanding the message
#owner:. However, the console does not provide much information about the stack where the error is found.
This is annoying. So, I set myself to find the method that generates the problem. This is a useful input for others that would like to help fix the issue.
In other words, I have a spike assessment on my hands. In this case, it is not the amount of code that is the problem, but rather the amount and intricacies of objects available at runtime.
There are multiple ways to approach this problem. I can look at the senders of owner:, but there are several possible candidates, and given that I not know well the code, it is hard to identify the root cause. I need a debugger, but how to get it given that the problem is in the debugger in the first place?
Let’s see. What else do I know? I know that the message (
#owner:) and the receiver of the message (
nil). What if I just create this method and inspect the context?
thisContext contextStack inspect
I run a code that pops up a debugger, such as
self halt, and the inspector appears.
thisContext happens to provide all relevant information for the current stack.
This was already great. But, now I want to see the source code of the selected context. The context already has the method, and the method can already display its source. I open the Methods presentation and add a little method:
self method gtInspectorSourceIn: composite
I save. The result: a little static debugger. If the live debugger is gone, this little tool can be a life saver.
One lesson learnt is that having interactive tools is essential during assessment. In my case, once I noticed that I could use another presentation, I could extend the inspector from within the inspector. The context switch was minimal. Once I saved, the inspector was updated and I could continue inspecting from where I left. The whole process was measured in minutes.
But, the most surprising lesson came from the realization of the incredible power that lies in Pharo and Moose. When such core things like a stack, or extending nil can be so easily manipulated, you should know you are in front of a special system. It changes the way you think about programs. Even if I develop these tools myself, I am still surprised of their power.
We are at a tipping point. Redefining programming is inevitable from this point on. We call it live programming.
We are happy to announce version 4.9 of the Moose Suite: http://moosetechnology.org/download/4.9
This is a minor release still based on Pharo 2.0. The differences from Moose 4.8 are:
- The Mondrian engine was removed. The transition to Roassal is now complete.
- Small fixes in Roassal layouts and rendering.
- Small fix in FAMIX regarding manipulation of accesses.
- Small visual fix in Glamour for rendering splitters in tables.
A list of issues addressed in this release can be found at: http://code.google.com/p/moose-technology/issues/list?can=1&q=status=Fixed%20milestone=4.9
The Moose Suite 4.9 comes for each platform as a separate bundle:
The Moose Suite 4.9 can also be loaded in a Pharo 2.0 image either from the Configuration Browser, or by executing the following script:
smalltalkhubUser: 'Moose' project: 'Moose';
((Smalltalk at: #ConfigurationOfMoose)
project version: '4.9-snapshot') load
MSE is the Moose textual format for serializing and materializing models and meta-models. It is a simple format that essentially looks like nested Lisp lists or Smalltalk literal arrays. MSE is supported through the Fame meta-engine, an engine originally created by Adrian Kuhn.
A typical example of a model is a code model that conforms to the FAMIX language independent meta-model. Such a model can be produced by an external tool like VerveineJ, or inFamix and then can be imported into Moose.
How does this work?
To go into details, let's pick a simple non-FAMIX example. Often, people that heard of FAMIX tend to think that Moose handles only such models. However, MSE is a general format being able to handle any model. Here is a different example:
mse := '(
(name ''The librarian'')))
(title ''Deep into Pharo'')
(authors (ref: 1) (ref: 2) (ref: 3) (ref: 4)))
(title ''Pharo by Example'')
(authors (ref: 5) (ref: 3) (ref: 6) (ref: 7)))
(LIB.Person (id: 1)
(name ''Alexandre Bergel''))
(LIB.Person (id: 2)
(name ''Damien Cassou''))
(name ''Stephane Ducasse''))
(name ''Jannik Laval''))
(name ''Andrew Black''))
(name ''Oscar Nierstrasz''))
(name ''Damien Pollet'')))))'.
In this case, we deal with a library model that handles entities like Book or Person. To load this model into Moose, you need to have a meta-model first. For our case, the meta-model happens to exist already as a default example in the LIBRoot hierarchy that comes with the Moose image.
For example, if you want to materialize the above MSE string into Pharo objects, you can execute:
repository := LIBRoot mainLibrary model importString: mse.
The result is a Fame repository. The repository has two variables. The
elements variable points to the model objects. The
metamodel variable points to the meta repository.
In our example, we have 11 such objects as seen in the screenshot below (showing the result in GTInspector).
The objects are created with proper connections between them. The below visualization shows the connections for our case: each bubble is an object, and each connection represents a reference between two objects.
As mentioned before, the
metamodel variable from a repository instance points to the so called meta repository. If we inspect this meta repository, we also see a kind of repository with 10 items.
These items no longer represent the objects from our model. Instead they represent the types from our model. For example, we have a
Library description and a
Library.librarian property. These objects are used by the Fame engine to represent objects models internally and they provide the information necessary for handling the import-export logic.
But, wait a second. If a meta-repository is a repository, can we not serialize it in MSE? Thanks for asking. Of course, you can.
MSE is a generic format. This means that you can serialize meta-models, too. For example, our library meta-model can be exported easily by invoking
exportString on the meta repository:
LIBRoot mainLibrary model metamodel exportString
This functionality is particularly useful when dealing with models and associated meta-models that are provided by external tools. In this situation, the steps to follow are:
- Import the meta-model in a meta repository.
- Generate Pharo classes from the meta repository.
- Import the actual model.
Perhaps this sounds complicated, but in reality it is rather straightforward:
metaMse := LIBRoot mainLibrary model metamodel exportString.
tower := FMTower new.
tower metamodel importString: metaMse.
generator := FMDefaultCodeGenerator new.
generator visit: tower metamodel.
The above script, loads the meta MSE and then invokes the code generator for it. For our example, we chose precisely the MSE file representing the Library meta-model. Executing the script reveals a browser offering a preview of the code generation.
Once the code for the meta-model is generated, you can rebuild a meta repository and load the MSE file with the model. In our example, the meta-model consists of three classes
LIBPerson. To build the repository, you can use the existing pragma processor:
pp := FMPragmaProcessor new.
pp queue: (Array with: LIBBook with: LIBLibrary with: LIBPerson).
tower := pp asTower.
Afterwards, you simply load the MSE with the model objects as you did in the first example:
tower model importString: mse.
The Fame functionality is at the core of model manipulation in Moose, and through MSE it makes it possible to handle complicated models and meta-models. By default Moose focuses on code related meta-models, but the underlying functionality is generic.
For simple meta-models, a simple inspector and code browser can be enough. For more complicated meta-models, such as FAMIX, Moose offers a dedicated Moose Meta Browser that can help you deal with multiple facets of a meta-model including inheritance, derived properties (these are properties that do not have to be serialized), or declared types. For example, the screenshot below shows the browser displaying the details of a FAMIX entity.
Meta-modeling can appear difficult at times, but as this post shows, if you put your mind to it, it does not have to be especially when you get an engine like Fame that is both compact and easy to use.
A software engineering myth says that if a system is legacy, you do not need to comprehend it. Instead, you are often advised to wrap it and work with it through a facade without worrying what happens underneath the black box.
But, as long as you still need something from your legacy system, you still need to understand at least that something. After all, many companies can live out of charging support even if they give you a free black box system. You do not need to modify that system, but you still need to figure out how it does what it does.
Take something like JBoss. If you use it, you might not consider it legacy, but you likely work with it through the advertised facade. But, once you do anything meaningful with it, you get to learn what it takes to understand what happens in its dungeons.
The only system that does not require comprehension is a dead one.
Instead of pretending that you avoid comprehension, better get good at digging up what you need fast and cheaply. It's more profitable. And more realistic.