May 28, 2006

Acme Corp. Do something.

Take any big company ad slogan:
  • HP. Invent.
  • IBM. Think.
  • Apple. Think different.
  • Samsung. Imagine.
whenever it has instructions in it - to think, invent or do anything in particular. What a heck does thinking (even thinking differently) or being able to invent has to do with the commodities those companies produce ? What does it mean at all ?

Do they really want to say - "Buy our stuff and then do whatever nice things we are telling you to do" ? That would be ridiculous. Do I need permission to think ? Do I have to buy something IBM to apply ?

"Buy our stuff and then you will start doing it no matter if you want it or not" ? Buying a Mac unlikely will make you think differently. Acquiring different GUI habits may be, but not different way of thinking, otherwise nobody would ever be able to interact with Apple owner from the moment of the purchase on.

"Buy our stuff and then doing those things would be easier" ? Nah, I doubt if inventing with HP is easier than with anything else.

Oh, I get it, I'm probably supposed to associate HP with inventions as such ? IBM with thinking ? Whatever company with whatever pleasant activity ? Now, THAT would be really stupid, just to think that there is an association between buying some piece of equipment and engaging in any pleasant activity. Otherwise, where are "IBM. Think, do you really need it ?", "Samsung. Imagine something not made of semiconductors" or even "Don't believe everything you hear in commercials" or "Trash your TV and go see the sunset" ? That last one, wouldn't it be a pleasure to do ?

Anyhow, TV commercials, magazines ads and equipment cartons would be the last things I would use for motivation, thank you very much. Except for blogging.

May 24, 2006

So, how does it feel writing infallible code ?

I think many people feel uncomfortable about the code they write or maintain, systems they support or administer. The stuff is just bad, let's face it. But people (including myself) tend to find a comforting spot in which they are most emotionally stable and least disturbed about the reality they have to live in. But this comfort is often based on ignoring the truth. Let's see how it may work in one particular case.

I had this idea when thinking about moving one very small but very common piece of functionality into a network service. We have each system to determine whether a given day is a workday or a holiday. As there is no such stable rule, each system has its own volatile piece of data in whatever format which it consults, and every administrator of every system has not to forget to keep it in good shape. It repeats in tens of variations, burdens the developers and is a constant administrative pain. Sounds like a good idea to factor it out into a network service ?

Paradoxically, it does not, precisely from the point of view of those troubled systems' developers and administrators. But why not ? It seems odd. Before the refactoring they have

function workday(date):
return consult_own_database(date) == "W"
...
if workday(today) then do_stuff()
...

whereas after refactoring they have

function workday(date):
return consult_magic_service(date) == "W"
...
if workday(today) then do_stuff()
...

(mind you that this a pretty real-life looking code). It looks like nothing changes at all, except for now they have one less problem. To simplify, let's assume such change wouldn't require any work from them, will be done in a snap and cause no troubles in itself. Still they refuse. What is the likely reason they give to cancel the change ?

- It's less reliable. A call to a local database (or filesystem) is less likely to fail than some network service.
Reliability, that I understand. And it's also reasonable to assume that the probability of local database failure is less than that of a remote network service. But I argue that it's not the different degree of reliability that matters here. After all the above chunks of code are identical, why would one be less reliable than the other ?

The first (existing) piece of code does not indicate a less probable failure. In fact it indicates a zero probability of failure. The call may fail, but the code does nothing about it. The developer consciously or unconsciously knows that there is a possibility of failure, but as soon as it's perceived below certain threshold, she's still comfortable ignoring it. Now with the subtlest change comes an increased failure probability which is too uncomfortable to ignore.

And what are the options ? Rewrite the code so that it accepts failures ? Impossible, too difficult, can't be done, no way, period. Thus we don't need the change, it will break the system which works fine. Ironical, isn't it ?

And so the thing remains unchanged, with regards to this switch to a network service or not, it becomes too fragile - touching it is dangerously uncomfortable. Therefore the point of this argument is that developer's comfort based on false assumptions, such as improbability of failures, forbids any major changes in the software.

May 06, 2006

The pattern to rescue other patterns: The Golden Hammer

Patterns look like they can solve all of your real-life problem. To me, this is awfully wrong and can do more harm than good. Yes, patterns allow for organizing your knowledge and also for developers to find commond ground easier and they do awesome job there. But they are nothing more than a mnemonic constructs.

I sometimes hear sentences like

- We use pattern X here, because it's the right thing to do in this kind of project.

Not because the solution has the appropriate structure, and pattern X somehow emerges from it, but because such and such problems require such and such patterns. What a heck ? I thought problems require solutions, not patterns. To me, such talk is a bad sympthom - it means that people can't see forest for the trees. Or, which is also possible, that I can't understand what they are saying, but let's forget about this insane assumption at once :)

Now, for pattern lovers - the pattern to the rescue - The Golden Hammer. It says that if you are good at hammer work, all your problems appear to you as nails. Likewise, if you are good at patterns, you find the patterns you already know everywhere. The key point here is that you do not attempt to apply thought and find other kind of patterns, but blindly say - hey, this obviously asks for a bridge, and this is undoubtedly a mediator here.

It appears that most beautiful and influential things appear with reflection - a tendency to analyze oneself. The Golden Hammer is most reflectional as it implies you actually think about the process of applying patterns (including itself). And if you are more conscious in applying patterns - you benefit more, don't you think ?

May 03, 2006

Expecting software errors, not fixing one and pretending none left

There is a problem with your software, something strange is going on, some error appeared that has never had appeared before, or should have never appeared in the first place, the system produced insane output or just died ?

It's unwise to think about anything you are experiencing as of "unbelievable". You see it, you have hard evidence - it exists. Why is that - you have no idea, but it exists. Finding out the origin of such a problem requires a totally different approach to debugging. As now you have no idea why it happened, you likely could have never predicted it, tested for it or even thought about it.

Stop and think about it. You could have never thought your software would fail in that particular way. Taken to the extreme this would mean that you never thought your software could fail at all.

But this is exactly how novice programmers think. As they become more experienced, the amazing world of software failures appears before them, and they start applying the typical code/debug cycle, error handling and/or try/catch patchwork, which still limits them to the errors they are experiencing at that single moment.

It seems logical that if the programmer's experience is taken to another extreme, she would expect failures everywhere, just for the sake of vigilance. No matter if a failure of some kind has never happened before - it's there, waiting to happen.

The point of this argument is that the key thing to writing stable software is being failure-proactive and expecting everything to fail. Just ask yourself pointing finger to any piece of your system: "what happens if this fails (for whatever reason) ?" The results can be far reaching.

I therefore argue that the common debugging is far less important a process than it's usually thought. Of course, finding an error and fixing it is important and must be done, but if you had expected it, your whole system is by now prepared to any error anywhere, and this one error only challenges this fact.

Moreover, I also argue that trying to break your own software is at least as valuable a practice as the forementioned debugging.

Yes, it's a well known maxim that all testing is about breaking things, not about seeing it working. But there is a catch. Tester can only test against problems that he can think of, so much like the developer. Moreover, as soon as developers and testers work side by side (unless it's the same person), they tend to share the same narrow field of view, which greatly reduces testing efficiency.

That's why I like stress testing. Not because it shows me impressive transactions per second, but because it tends to reveal another layer of problems usually never seen. Stress testing is very useful, but it shines whenever a failure leads to a cascade of other failures. Whoa ! I couldn't have thought of THAT !

Now, how else you can intentionally break your system except by giving it a stress load ?

Failure injection comes to mind, but it contradicts to the normal development cycle (as there is simply no place for it), and requires the mentioned failure-ready system structure from day one. I also tend to remove the injected failures in production system, after all being a failure-vigilant software developer won't save you from black cats and broken mirrors.

Unplugging cables from the working system is also fun and can reveal yet another unexpected problems, although again, it's probably not applicable to a production system. For another joke of this kind see "The China Syndrome Test" in "Blueprints for High Availability" by Evan Marcus & Hal Stern.

In conclusion, this discussion again turns me to the PITOMNIK principle: as the system grows, its small errors grow with it and therefore lead to inevitable failure. You cannot ignore the presence of the errors, nor you can fix them any significant fraction of them, so you'd better be ready.

April 29, 2006

Software: too many layers, too few tiers

Layers and tiers are both ways of enforcing structure onto a software system. I will be using term layer = "logically isolated group of modules within a process" and tier = "physically isolated group of modules within a system". The difference is basically logical vs. physical.

Now, layers are way more popular and friendly to any development environment, because they
  • are directly supported with the language and/or runtime
  • allow greater flexibility and freedom of interaction between different layers
  • are accustomed way of thinking about software
OOP with classes and interfaces are all about layers.

Tiers, in turn, are more difficult to deal with, because they
  • require more upfront design
  • require complex and/or restrictive and/or expensive (un)marshalling
  • require different way of thinking
There also are obvious upsides to tiers, mostly about independence, ex.
  • deployment and execution independence
  • development, language independence
  • reuse independence
CBD with components and interfaces are all about tiers. Right now the choice is basically limited to a few popular component-based environments, such as DCOM, CORBA or (somewhat differently) web services and SOAP.

I sincerely believe that having more tiers is beneficial and it would be great if there was a way of making tiers easier to use in a way similar to layers. And so, guess what, this is one of the ideas behind the Pythomnic (Python framework for building reliable services) - to allow fast and easy way of converting layers into tiers - to mix and match modules in any way.

For example, if there is a cleanly separable function (ex. CPU intensive XSLT transformation) currently allocated to a module or a set of modules in a layer, you may also take one step ahead and declare that this function can possibly be separated to a different tier. For example, instead of calling
pmnc.xslt.transform(...)
you do
pmnc.execute.on("xslt_transformer").xslt.transform(...)
the key thing to note is that the way execute.on works, if "xslt_transformer" RPC channel is not configured in an appropriate configuration file, the pmnc call will still be local, but as soon as you modify the configuration file and save it, the very next call will go to the specified server. There is no need to restart Pythomnic on this machine, all you need to do is to copy the xslt module to a separate server and start it there in its own Pythomnic thus turning a layer into a tier.

I do believe that such a feature is beneficial to a middleware development framework.

April 23, 2006

One note on Python simplicity in handling phoenix problems

One of the major problems in developing long running applications (and applications in general really) is cleanly handling shutdown. Among them a particular subproblem of phoenix singletons - what happens sometimes if one entity references the other (typically a global singleton), which has already been unloaded.

For instance, consider the following shutdown code in moduleA (using simplified Python syntax):

moduleA:
...
def shutdown():
moduleB.callme()
there is no guarantee that moduleB has not been shut down yet. Now, if moduleB is also written in a delayed initialization fashion, ex:

moduleB:
...
impl = Impl()
...
def callme():
if not impl.initialized():
impl.initialize()
return impl.callme()
...
def shutdown():
impl.cleanup()
then what happens upon moduleA's shutdown is a reinitialization of the impl - one sort of a phoenix. It just went on me that instead of building a complex synchronization schemes to handle this cleanly, all I need to do to prevent this is just

moduleB:
...
def shutdown():
impl.cleanup()
del impl
and now as impl is just not there, the moduleA's attempt to reference it at shutdown will fail and throw - a much more appropriate behaviour in a given situation. This is less clean a solution, but how simple it is !

April 19, 2006

Shortcuts to icons to shortcuts: what happens if you click that ?

It seems that desktop icons have rather confusing nature. Let's say there is an object, e.g. executable file. Whenever it physically presents on the desktop, it appears as an icon. Whenever it's not on the desktop itself, which is a more frequent situation, but a shortcut, an object of different kin is, the shortcut appears as the same icon albeit with a little arrow in the lower left corner.

Hence problem #1. There are different icons on the desktop with and without arrows, but single-clicking on them reveals identical behaviour - the target object is invoked. What arrow is for then ? For the user to see which is "real thing" and which is a representation of a concept she can't quite grasp anyway ? Wouldn't it be more logical if non-shortcuts couldn't be placed on desktop at all ?



Next, surprisingly, there are other places on the desktop where icons appear - task bar, quick launch bar and system tray. Those add to the confusion, because although their icons look identical, their behaviour is even more different:

Icon on the quick launch bar represents a possibility to start an application, clicking it starts another instance. Icon on the task bar represents an already running application, clicking it brings it to front. Icon in the tray represents an application willing to talk, but what happens if you click it is not known beforehand.

Hence problem #2: wouldn't it be appropriate if all these icons were different to some degree, or behaved in more consistent fashion ?

April 18, 2006

On (information security) audit: giving money to the developers

Auditing information systems is fiendishly difficult. Think about it - a typical situation for a developer is discovering problems in the _small_ pieces of code that she's working on _right_now_. Few days later other problems may be discovered. Half a year from that - yet other.

Then, as the system is assembled, parts developed by different people come together, a whole new world of problems emerge. The people who built it have scattered knowledge of the system themselves.

Now, to audit. Suits come in, unpack their laptops, run standard tests, look (!) at everything and ask tough questions. A week after they conclude whether the system the very authors have no complete knowledge of is good or not. And then they leave.

Hence my point - a good team should be doing internal audits as it goes. A good developer should be running custom-tailored tests, looking at the thing, asking tough questions no worse than the auditors. And the knowledge remains with the company.

Therefore, why not investing the same money into team education, so that they become their own auditors ? It's the old "give fish" vs. "teach to fish" thing.

I realize there are PR and sometimes legal aspects to audit, but to a developer PR along with legalities don't make much sense.

March 26, 2006

Note to self - Python properties are non-polymorphic.

Properties are just one application of Python ubiquitous proxying. The properties are created as wrappers over get/set methods:
class C(object):

def __init__(self, value):
self._value = value

def _get_value(self):
return self._value

value = property(_get_value)

# all is fine, value calls C._get_value:

assert C(10).value == 10

Here is what's happening: an instance of a property class is created and a reference to C._get_value is stored in it. Next time c.value is referenced, the property calls the original method.

The problem is, the reference is bound to the particular class C, and so if you inherit from it, you should not expect the property to be rebound to the ancestor class'es method:
class D(C):

def _get_value(self):
return self._value + 1

# now, this still calls C._get_value:

assert D(10).value != 11

The properties are thus non-polymorphic. There is one way to make them such by introducing late binding,
class C(object):

...

value = property(lambda self: self._get_value())

but I personally find it cumbersome.

March 24, 2006

Moore's law as a social amplifier

The hardware world lives by the Moore's law of constant speed up. The software world is following the same trail, but there is significant difference - hardware measurably increases performance while staying on the same line. Dragster style if you like. Compared to this software is a bunch of beer cans hanging off the racer's bumper:

It makes a lot of noise. So much noise, 90% of the humanity can hear it, for God's sake.

More stuff can be added as the car's engine gets more powerful. What for ? Heck, why not ? After all, people prefer dancing pigs.

It can hardly choose the way. As the principal architecture remains the same, the algorithms remain the same, people have no need in extending body of knowledge and software thus becomes a self-fulfilling prophecy.

It passes everything at such a great speed, there's no way to ever see what's in it. Who cares, next year pack will be better. Likewise, when you are inside the can, all you can see is wind. Speed, more speed !

Anyhow, the question here is - are people strong enough, knowledgeable enough and not the least - moral enough to deal with Moore's law, to use such a powerful engine ? Are they using it and for what ?

My answer is - Moore's law is a great social amplifier.

It gives you incomprehensible powers. And then it's up to you. Dancing pigs, be it. Just riding the can, good. Sending a billion worth of ads with a single click ? Yes, sadly you can do that. Moreover, as computers control everything, everything becomes a beer can. Attitudes change in all industries, and this is sad, as there are areas which cannot be sped up at that rate (if at all).

But !

You can always take off and observe the way where it's all going. Or may be even forget about the race and do some beer can art (that's what I personally prefer).

On average though, having more power means less thinking and more chaos. Secure about the future ?