I’m getting ready to work on a large Zope 2 system that we’ve developed in house over the last few years. This system is a bit of a framework in its own right, with some inspirations coming from the “Component Architecture” influencing bits of the CMF and also from Zope 3 itself (while Zope 3 was being developed). This internal framework has long since moved from code managed through the web and into Python based products. It’s fairly modular, and we’ve even been able to bridge a couple of very different systems together using some relatively smart architectures.
I was looking at some code I wrote for this system. This code is comparatively recent and lightweight compared to what is in the core. I noticed something as I was looking at it though – I felt really claustrophobic. It’s not that it’s bad Python code or anything, really. But it was just claustrophobic. Methods were too long. Lines were too long! There were too many strings, strangely enough…. There was just something off. And again, I must say that it’s not bad code. It’s not being too complex, too tricky, too verbose, nor is it riddled with shortcuts. There was just a gut feel that didn’t sit quite right with me.
I decided to do a quick comparison against some of the recent code I’ve written for Zope 3 based applications. I decided to check against some of the more hairy “written under deadline” code at that, and I tried to find comparable situations (doing custom adds / edits). The verdict? The Zope 3 targeted code did not feel so claustrophobic! At all.
Now, maybe it has something to do with the older code feeling more alien and the the new code feeling more fresh.
But I think it has to do with something else that I’ve noticed about Zope 3.
I think it’s easier to chunk code up into smaller units of work in Zope 3. I think it’s easier to refactor by doing common refactorings like “Extract Method” and not have to pass as much information around. It’s easier to look up adapters and utilities that provide certain functionality instead of having all of that functionality heaped onto an object through a large inheritance tree.
And it seems like it’s really easy to abstract out common concepts into helpful tools and utilities, which grow into ones own helpful module, toolkit, or framework. At least, it’s easy in comparison to Zope 2.
I certainly know that I’ve been able to easily extract out helpful tools from common actions and update my Zope 3 based code to use those abstractions. And every time I do it, my code gets tighter and more powerful and then taken for granted since we no longer have to worry about it.
For example, a few months ago I worked hard on making a nice “undo” interface. Many ZODB storages can undo actions at the database layer. As I saw applications like GMail provide a nice “Message discarded (Undo Discard)” message for certain actions, I thought “I’d like to provide that, and hey! the ZODB can already undo things for me so I don’t have to track them myself!” So I went to work on providing a nice undo message. I ran into some snags along the way (related to things like how I was using sessions to send page messages to the user), but soon got it all buttoned down nice and neat. I was able to wrap it up in a nice API and handle deletes like this:
name = zapi.getName(item) message="Deleted %s" % name del self.context[name] undomsg = cmsapi.undomessage(self, message) cmsapi.flash(self, undomsg)
After I checked this work in, I also added it to our oldest Zope 3 code, a content management system, in just a couple of lines, and got rid of an annoying Javascript alert box in the process. And then I promptly forgot about it until I was watching a co-worker using the CMS yesterday and saw the “undo delete” message. And watched it work flawlessly, with even the undo being undoable.
The point of this? Zope 3 eases refactoring and restructuring. I’m still amazed at how quickly our content management system came together, and how easily it was to separate out the stack into increasingly vertical levels. We almost didn’t even have to think about it. During post-mortems after major releases, consolidating code that had been copied and pasted into three different customer libraries into a single unit, without breaking our customer’s sites, was almost too easy. And on more than one occasion, just after such a restructuring had happened, a feature request would come along that was easy to apply due to that recent restructuring. “We need to make all of these things cacheable.” “Wow, a week ago that would have been four times the effort had we not boiled all of this stuff down into a core package and updated everyone already.”
And I think that’s the difference I notice between the code I was looking at today from our well-factored Zope 2 efforts compared to our more recent Zope 3 ones: we’ve been able to get rid of a lot of the line noise in our Zope 3 code, and much of the good Zope 3 code (like ‘formlib’) does a good job of clearing out line noise already. Fascinating.