Joel Spolsky is the president of Fogg Creek Software and frequent commentator on the software development industry. His latest article, Architecture Astronauts, criticizes Microsoft's continued re-invention of something no one seems to want.
Read Joel's article to get the full comic affect, but here's a pertinent excerpt:
When did the first sync web sites start coming out? 1999? There were a million versions. xdrive, mydrive, idrive, youdrive, wealldrive for ice cream. Nobody cared then and nobody cares now, because synchronizing files is just not a killer application. I'm sorry. It seems like it should be. But it's not.
A killer application would certainly be the next big thing. If you're unsure what a killer application is think of the first word processor, spreadsheet, or database program. Some of you may not appreciate the impact a killer-application can have on the world because the last killer-application was Tim Berner Lee's introduction of the World Wide Web in 1991--17 years ago!
As it relates to "the next big thing" or what users really want, after reading Joel's essay two things popped into my mind immediately. The first is my frustration with needing a different user ID for every website that requires registration. As if to add insult to my injury when I went to make a comment on Joel's essay on Reddit I had to create Yet Another Account Profile (YAAP). I was reminded of the next while reading other users' comments I noticed how poorly discussion forums are implemented as web applications.
There are many companies and portals that pretend to provide single sign-on. The idea being that users create a single account including user ID and password and are automatically credentialed for multiple applications across the internet. The problem I see with the current approach is two-fold. First, I don't trust many companies with being the guardians of my "official" profile due to my suspicion of their ulterior motives. Will be profile information be sold? Will it be harvested by advertising companies? What will the company or their "partners" do with the information about other sites I authenticate to using their credentials?
Microsoft Passport wanted to be a single-sign-on for the internet, but Microsoft had already demonstrated their contempt for users making it so difficult to verify the authenticity of my Windows license when simply upgrading my computer--much less throwing it out and replacing it with a new one. Even Microsoft seems to have admitted Passport's reputation by dropping it. Of course, not willing to let go control completely they re-invented it as Windows Live.
Do you really want to trust Microsoft with your profile after their Orwellian Windows Genuine Advantage patch?
There are entities I might be willing to trust. First in the US Post Office. Already we trust them to deliver our mail, both first class and bulk, desirable or not, and best of all--everything is brought to my door-step by a uniformed representative of the United States Government.
Perhaps out of necessity, I also trust my bank. Even if it is out of necessity, my credit union hasn't given me cause to believe they want to own me. Instead, my credit union (and bank before that) actually trust me with their money for my credit card, car loan, mortgage, and home equity LOC.
It's a place to start, anyway. OK, two places to start.
I'll discuss the next thing in the next article, I'm thinking of calling "The next big thing should stop ruining the last good thing."
The Big Three VCs?
Michigan is home to three of the largest, potential venture capital companies in the nation; Ford, Chrysler, and General Motors. They're surrounded by a cadre of other potential venture capitalists that go by the names Johnson Controls, Lear, American Axle, Eaton, and others.
Automotive VCs have superior qualities traditional VCs do not have. VCs may have money but what startups need as much or more than money is customers--and the Big Three are some of Michigan's best customers for a startup. Many start-ups attract enough investment capital to birth their innovative products, but starve later without customers. This is where automotive VCs can step in. Not with their investment dollars, but with their purchasing dollars.
The auto industry and the entire state of Michigan have a lot to benefit from The Big Three VCs. Beyond the Big-3's research, development and ideas to become more efficient, are hundreds of companies that have researched and developed ideas on their investors' dimes the Big-3 may benefit from. To paraphrase an industry saying, wherever the auto industry goes Michigan follows.
Because Michigan's auto industry is under assault from foreign competitors, environmentalists, employee benefits obligations, US lawmakers, and what feels like the entire state of California, it's possible they've taken a defensive posture which precludes entertaining many ideas from outside their comfort zones (if they've any of comfort zones left).
A start-up's life summed-up in a 30 second commercial
Our company started back in 2000 after the internet bubble peaked but before investors knew it had burst. Anyone trying to raise capital for a startup knows how difficult a period that was to raise money. I wrote briefly about on May 2006 in the article, Angel investors are more than their money.
For what seems like a few years I've been enjoying CapitalOne's marketing campaign, What's in your wallet? Some of the things they did with those barbarians were darned funny.
One of their most recent commercials, with the princess kissing the frog to get her rewards reminded me of two things--raising money and finding customers.
I was discussing the commercial with an angel investor familiar with VCs. He told me it reminded him of how many VCs will never say, "No," but most of them won't say yes either.
In the commercial, the princess kisses the frog hoping for a prince. Instead, it turns into a ferret. It makes promises about the next kiss but turns into a turkey. More promises, another kiss, and it's an orangutan. Finally, an unattractive middle-aged centaur pleads as she leaves, "Your just one kiss away!"
It reminded me of another conversation I had with an angel investor familiar with how VCs work. He told me most VC will never say, "No." Many will never say, "Yes," either, but they'll make promises about the next meeting, or the next business plan, or the next phone call, or ...
It's hard then not to compare raising money with finding customers. In Instream Services' early years I remember how excited we'd be when we met with a tier-one supplier. They'd admit they needed our service. They'd admit their suppliers needed our service. They'd even admit how easy our supply-chain finance program would be to implement. We'd have meeting after meeting, each more promising than the last, until finally they'd decide to either do nothing, or do something less valuable with a bigger company.
Desperately seeking Susan
In a recent Detroit News op-ed, Region must show the money so entrepreneurs will succeed, Samuel Valenti III, executive chairman of TriMas Corp., president of Masco Capital and a board member of Detroit Renaissance, encouraged Michiganians to..
"...think outside the box and take some calculated risks if Metro Detroit is going to be competitive in the global economy. Risk taking is part of entrepreneurial spirit, while entrepreneurs provide the creativity that spurs economic growth."
I agree with Mr. Valenti, and encourage Governor Granholm, leaders from the state house and senate, and our automotive OEMs and suppliers alike to realize the potential of Michigan's greatest industry to accelerate entrepreneurial growth in a way that capitalizes on Michigan's unique resources and stops trying to imitate California's or Massachusetts'.
It's one thing to be inspired by another state's success. It's quite another to become so desperate we try to become another state.
A familiar echo
A colleague of mine has a theory why Vista requires 2GB of RAM and a late-model CPU to run satisfactorily. He believes this is likely the first edition of Microsoft's flagship operating system primarily developed in India rather than Redmond Washington.
Except for press reports of Microsoft's huge investments in China and India, and their outsourcing of development to those countries, I'm unaware of precisely what is being outsourced and what measures Microsoft has taken to insure a quality product. Quality being measured not just in bugs and resilience to breakdown, but the quality experienced programmers know can exist in the code itself. The economy of expression. Elegant algorithms. Brilliant structures and modularization. Unless Microsoft releases Vista's source code, which I think unlikely, we'll never know for sure whether Vista has the hidden qualities Paul Graham describes in his essay, Hackers and Painters.
The shot heard round the boardroom
Our development team was asked by one of our largest investors to visit another company he owned and analyze their software, development methodologies, and testing procedures. No greater compliment could have been paid us. The company in question was on the verge of signing a large contract that had the potential for significant revenue growth and pressure on the existing software platform. Company directors were anxious about the deal because the software was showing significant signs of stress. When we visited there were over 800 bugs listed as critical. Among them were reports that took too long to be usable, some of their customers were able to see other customers' data, and invoicing was broken.
We'll skip the messy details, but there are some red flags that predicted their problems. To protect the innocent and guilty alike we'll call the company Newco.
The good
Newco had a great start. Their innovative web-delivered service was easy to learn and use. They didn't need the overhead of a sales staff because the service was self-enrolled. Membership included newsletters with helpful articles both on using the system and advice from industry professionals. Additionally, because the service required only an internet connection it was priced competitively and easily won business from other providers.
The bad
Curiously, Newco's management had no previous experience in either their product's industry or software development. They created the service and attracted quality investors, but that was pretty much the end of their most valuable contributions.
Neither Newco's or their directors realized they were in the software business. True, the service wasn't software related, but the entirety of Newco's intellectual property was invested in the software. The danger of not knowing what business you're in is loss of focus. In this case the loss of focus wasn't a mere distraction, it was completely misdirected. Instead of jealously guarding and nurturing that which defined their company, the software, their attentions were elsewhere. From the beginning, software development was an expense to be minimized rather than aggressively invested in.
The ugly
Newco's management was filled with large-company escapees that approached small-company's software development the same way a large company might: simple project management. All they had to do was to find inexpensive labor, describe the requirements, agree on delivery dates, and hold the developer to them.
Their CTOs were either not experienced developing software or weren't given the opportunity. The last CTO had no experience writing or designing software (or in Newco's industry) but instead had many years experience managing projects at a large IT consulting firm.
They peddled their IP for development to outside contractors across three countries and two continents--none of them domestic. This isn't an indictment of the quality available from overseas developers, but evidence of how far away geographically and culturally they dispatched their company's jewels. All the time they did this they didn't have in-house technical expertise to measure or critique the software's design or engineering.
Ultimately, Newco lost complete control of the software. It's design, it's host operating system, the database, development tools, infrastructure tools, language, and issue tracking. In short, they'd lost their ability to be self-deterministic and had become completely dependent on other parties for their survival. By the time we arrived their own intellectual property was completely foreign to them both literally and figuratively.
The clever bookend
Which brings us back to Redmond. If my colleague's suspicions are true what might that say about the business Microsoft is in? It may be they're perfectly capable of managing off-shore development with greater competence than Newco possessed. Or it may indicate an significant change of direction for Microsoft--demonstrating it's no longer in the software development business as much as it is another business, perhaps the patent and property protection business?
Microsoft is certainly a large company. Perhaps one of the largest. It's certainly exercised its marketing, legal, and acquisition might and expertise with the financial resources to back them up. And now that its head is turned toward other activities unrelated to the actual exercise of writing its own software has created an opportunity for other companies that are focused on writing their own software and jealously guarding it to establish a beach-head that wouldn't have been imaginable not too many years ago.
Can you say Google?
Newco was eventually sold at a discount to a competitor for the only thing it possessed worth paying for--its customer list.
A recent ACM Queue Advice Column by Kode Vicious, called Advice to a Newbie, asked:
Do you remember the first time? The first time when, after struggling with a piece of code, you felt not only "I can do this!" but also "I love doing this!"
I still remember that rush. It was addictive. When it happened I decided what I wanted to be when a grew up: a computer programmer.
You can read the rest and leave comments on my IT Toolbox blog.
eWeek recently published an article titled, Why Big IT is a Different Place to Work, that lists some of the differences Forrester Research found from a study comparing big shops to small ones.
Some are obvious, like their size. But Forrester identified other differences that make little sense.
The most evident difference between big and small IT shops is that which defines them: their size, and therefore, their visibility.
"They also have large-scale systems and when they make changes to them, it gets a lot of press," Marc Cecere, Forrester vice president, told eWEEK.
Big companies get big press because they're big. Big companies make big targets, big targets mean big names, and big names make big headlines. A journalist can't go wrong reporting what's happening at Wal-mart, Home Depot, or Ford Motor. It's a safe story. Whatever they're doing impacts thousands of users, has consumed millions of dollars, and requires months to complete. But because those companies are big their ability to endure poorly made IT decisions or poorly executed projects is also big. Articles may say the projects were high-risk, but despite the headlines of failed initiatives the companies are still there.
Not so with small companies. Smaller companies can be killed with bad IT. Smaller companies can't grow with bad IT. Smaller companies can't compete with bad IT. The IT decisions and projects small companies make are far more interesting because so much more is at stake. And honestly, there are more small companies than there are large ones so stories on make-or-break IT are likely to have an audience hoping to duplicate successes and avoid failures.
Why? Because IT makes a greater and swifter difference at small companies than large ones.
It's not just the size that differs in these IT shops, but the type of managers they tend to hire.
"They generally have more experienced management. The person who runs the infrastructure group generally has more experience and more knowledge than someone running a shop of 50 or so people," Cecere said.
I can't speak for all large companies, but having worked at some, worked with more, and from the anecdotal evidence I've heard from friends, coworkers, and at conferences, the talent big companies hire and promote is talent more skilled at corporate politics than their technical fields.
Not mentioned is the talent they fire--or don't fire. One of the big costs of big IT (or big anything) is the legacy costs carried for employees that should have been let-go years ago but have become permanent, under-performing, institutional employees. Everyone knows who they are. If the staff could they would vote them off the island. But the "more experienced" managers keep them around. Perhaps because "more experienced" managers know they can budget for a 4% staff salary increase then give those workers 2% and distribute the extra to better-performing staff.
Small staffs of "50-or-so people" can't afford dead weight. Their budgets are such that the managers know that paying under-performers actually takes away money from performers, or even takes money away from sales, marketing, operations, or any other group that could put those dollars to more productive use.
So who are big companies likely to fire? Trouble makers. After leaving big companies they're the ones likely to get hired at small companies where every skilled employee counts and every dollar must be wisely invested.
For years technical people's careers have been limited because to make more money they had to go into management. The article later states the big companies have bigger resources (surprised?) and more discretionary spending. Big company managers don't need to be working managers because they have staff to do the real work. So what are they hiring? Budget analysts familiar-enough with business and technology to make interesting quotes and help underwrite studies about how important big IT shops are.
Bigger IT shops have more discretionary resources at their disposal, which allows the IT department to have more time to develop resources and plan for the future.
"Small shops don't have a lot of slack. Everyone is so busy fire-fighting, nobody can take a look at what might happen a couple years down the road. They're too busy patching," Cecere said.
I hope everyone reading the eWeek article appreciates the inaccuracy of gross generalizations (mine, of course, all very accurate). I can't speak for all small IT shops, but InStream spends a lot of time thinking about the future because our business model depends on IT resources not growing proportionally to our outstandings (we're a financial services company). We want IT costs to be as flat as possible. Our technical operations group spends as much or more time automating for the future as it does operating the present. Our development group works hard to make sure the features added today won't get in the way of the features we may add tomorrow. Quick fixes (sometimes called kludges) are also carefully crafted to make sure they don't become obstacles or weak-links in the future.
"Big IT hires more entry-level people because they have these mechanisms for training in place. They tend to put people in more specialized roles. In a 50 person shop, you're probably going to be more of a generalist," Cecere said.
True, big companies have training programs and can afford hiring employees whose greatest contribution is their low-cost. But what's happened to the experienced people at the top? I'm confident that if Forrester dug around they'd find research suggesting the more experienced and skilled workers (read: older) a project has the more likely it is to succeed--or at least succeed on a technical level. Projects may still fail for plenty of business reasons, but that's not the fault of the technicians--it's the fault of management--the same management Forrester says big companies hire because they have more experience skills than smaller companies can afford.
Lastly, the Forrester study says big IT shops are more likely to outsource than small IT shops.
In Forrester's research, big IT shops were found to be more likely to outsource (45 percent versus 17 percent), consolidate applications (31 percent versus 25 percent) and empower a center of excellence (48 percent versus 28 percent) in the next 18 to 24 months to increase efficiency than their small IT shop counterparts.
From this information are we supposed to congratulate big it shops for starting projects to increase their efficiency or castigate them for becoming inefficient in the first place? Or should we wonder if the other IT shops not outsourcing, consolidating applications, or creating centers of excellence (whatever those are) are in denial or perfectly managed?
And what exactly is so bad about a little inefficiency? Having extra resources means having extra bandwidth. Extra bandwidth means IT can explore (formerly called R&D) new technologies, become better acquainted with the what's already in-house, or survey their user community to see what they might be asking for next, or imagining what IT might do to grow profits rather than just trim expenses.
Another reason they may outsource is to have work done by smaller companies "of 50-or-so people" that by virtue of their size may be more efficient, flexible, and motivated.
But of course, "more experienced" managers already know that, which is why Forrester says they're so valuable to Big IT.
Though it may seem like it, I'm not really resentful (really!). I'm both frustrated and concerned. I'm frustrated because I used to work in Big IT and knew we could do more with less. I'm frustrated because Big IT is too often incented to maintain the status quo and reduce expenses than to take risks and make investments. I'm concerned because I believe Big Thinking IT can make a Big Difference regardless the size of the company.
I'm especially hopeful for America's manufacturers, some of whom need some Big Thinking and I think Big IT can be the place to find it--if Big Companies would let them out of the box--no matter how big that box may be.
I returned home from Toronto yesterday where the Smalltalk Solutions conference took place alongside two other conferences at the Toronto Convention Center. Toronto is a beautiful city. If you've never been there before you should put Toronto on your short list of cities to visit. While you're there be sure to visit Jack Astor's at the corner of Front and University streets.
On Tuesday I presented my "There is no spoon" lecture for the first time. Afterwards, I had the opportunity to speak with several attendees regarding the challenges of object oriented languages and relational databases. I want to share a few of their thoughts and comments.
Bruce Badger asked if by using stored procedures to aggressively guard the semantics of the database made switching database vendors more difficult. The theory goes that if shops keep their SQL generic or hidden inside an object-relational framework then switching from Oracle to DBS, or MySQL to SQLServer is a non-event. I told the audience that if a shop wasn't already taking advantage of a vendor's unique features they were being foolish (foolish being euphemistic for the actual word I used in my response). Most companies don't select a relational database product based on price alone. They pick it because it has features they can benefit from that other products don't have. If after purchasing a product based on its features a company decides not to use those features I believe they're morons.
Other attendees were surprised (envious) that at InStream there's no division between application, system, and database programmers. Here, if a new feature requires a change to the database the same programmers that enhance the code can also change the database. Many can only dream of the increased agility their IT departments might have if their databases could be as accommodating as their programmers want them to be and their business customers need them to be. As important as good database design and integrity is, some software development departments use a division of responsibilities to protect the database from stupid programmer errors. I believe if you can't trust your programmers then you've hired the wrong programmers. Good programmers take as much responsibility and ownership of the database as they do their code. If managers don't think their programmers are capable of responsible database stewardship then they might consider getting new programmers.
Dennis Smith told me the presentation wasn't at all what he was expecting. He anticipated my describing some new framework or technique for mapping objects to database tables and rows that would hide the dreaded object-relational impedance mismatch. He was surprised at the lecture's assertion that when OLTP systems are designed to treat the database with the same respect OO programmers give their own objects that there is no object-relational impedance mismatch. The new approach, according to Dennis, gave him some ideas on how to address some designs back at the office.
I want to thank everybody for coming to the lecture, and sharing their thoughts to me both during afterwards.
I'm excited and honored to be invited to talk at this year's Smalltalk Solutions in Toronto. My presentation, There is no spoon, will be based on some of my earlier postings on this blog, especially The RDB is the biggest object in my system, and Databases as objects: My schema is a class.
I'd better get to work on my slides.
A recent ComputerWorld article, Heads in the Sand: IT isn't ready for the bird flu, by Robert L. Mitchell, paints doom and gloom for consumers around the world. Companies will be devastated, revenue will halt, and our overnight deliveries will be delayed.
Here are some of the alarming statistics from the article:
That last statistic provides an interesting perspective. In the "worst-case" scenario with a 60-65% mortality rate that would mean the deaths of 180-195 million people in the US alone. And ours is a "1st-world" nation with above-average health care, abundant potable water, and (mostly) contaminate-free produce. Imagine how the worst-case scenario may play out in other, less developed countries. Thomas Friedman may believe the world is flat in a technical way, but it certainly is not when it comes to sanitation.
Son, your ego is writing checks your body can't cash.
Companies that have chosen to outsource to countries at higher risk for deadly viral epidemics to save on software development costs or less expensive wheel rims are betting the money saved today is better than business continuity later. Or as Stinger told Maverick in Top Gun, "Son, your ego's writing checks your body can't cash."
With just-in-time supply chains, off-shoring, out-sourcing, and right-sizing (down-sizing, whatever) many companies have become very efficient (and we assume profitable) by optimizing for optimum conditions. But what happens when conditions change drastically and abruptly? If you only have as many employees as you need what happens when 20, 30 or 60% aren't able to work either temporarily or permanently?
Let's agree right away that not every business is critical to life, and in the face of millions of people dying around the world I can go without my favorite soft drinks or new TV advertisements for room fresheners. Even so, none of these companies investors or employees want them to go out of business either.
One way to prepare for the (we hope temporary) loss of workforce is to have extra workforce to begin with. Like having extra cash in the bank it's a good idea to have extra, trained, experienced employees. Besides spreading the workload across more people (relieving stress and overtime) more bandwidth means capitalizing on opportunities more "efficient" companies can't respond to. It means company-wide training doesn't affect production. It means professional employees can spend more time improving professional skills, reading industry rags, and attending local networking events while critical jobs still get done. It also may be the extra fat a healthy corporate body needs to survive an "inevitable" event which is how many scientists describe the bird flu pandemic.
I worked for a company that trimmed its professional staff to more "efficient" levels. Before the "optimizing" occurred the professional staff had time to read newspapers, magazines, journals, and other industry-related material that helped them stay on top of their (what's another word for..) profession. After the right sizing so much of their time was spent servicing their clients there was little time left to improve their service to their clients.
It doesn't hurt to have a little fat today or tomorrow. Corporate anorexia isn't a strategy, it's an image problem.
In my previous article I wrote that the database is the biggest object in my system. If that is the case, I should be able to test the concept against the Gang of Four's Design Patterns to see how the idea holds up.
But before doing that I need to define, in database terms, what classes are and what their instances may look like.
In OO terms, a class is a template that defines what its instances look like. Cincom's VW Smalltalk's Date class defines two instance variables, day and year. Given those two instance variables any Date class instance can keep track of a date.
My database has a schema. That schema can be executed as a sequence of data definition language (DDL) statements to create a new instance. In addition to our production database we have multiple other instances created with the same schema our developers and quality analysts use to test the system.
Part of a class' template defines its instances methods. Which operations does it support. What behaviors can a user of any of a class' instances expect to be available? Inside a class hierarchy classes inherit the behavior of their superclasses--the classes from which they derive their base behavior. A class can add new behavior or override inherited behavior to create an object with unique capabilities not available in any of its ancestors.
Before I extend any of my database' behaviors, it too, has default behaviors. At the lowest level I can use SQL statements to introspect and interact with my database in all kinds of low-level ways. On their own, these low-level behaviors know nothing of my application or its unique abilities and requirements. Like a class, though, I can add new behavior or even override default behavior using stored procedures and views to provide unique capabilities not available or impractical if they didn't exist.
In the world of Sybase, every database inherits the attributes and behavior of a database named Model.
Model
^
|
|
Efinnet By itself, this is beginning to look like a class tree--though a very shallow one. Something's belonging to a tree isn't more probably based on the depth of a tree (or its lack of depth). In fact, many OO designers are advocating for shallower hierarchies. In either respect, our database fits right in.
We already talked about instance variables and methods, but what are some of the other OO-ish things my database can do?
Persistence - One if its most important features is its ability to persist itself on disk and maintain its integrity. The entire state of my system is preserved and maintained inside my database object.
Introspection - My database can tell me things about itself, its variables and its methods
Composition - My database is composed of other objects called tables. Some of the tables were inherited from its superclass, others were added to extend its functionality.
Singleton - Instances of my database exist as singletons. For each instance of my system one, and exactly one, instance of my database exists to preserve and protect the state of my system.
Messages - The only way I can communicate to it is by sending messages to it. I can not (and care not) to manipulate its data directly at a low level (disk) because that would risk its integrity--not in a referential way but at a disk-level consistency way.
Extendability - I can extend my database's schema to define new variables (tables) and behaviors (procedures). Even better, I can apply the new schema its instances.
It's amazing it took me 20+ years to recognize the similarities between objects and databases. But now that I'm confident my database is an instance of my schema and in other important respects is in fact an object (singleton) of its own, I can start visiting various of the GoF's patterns to see how well they apply.
After posting a link to my The TOA of Tom a couple interesting discussions occurred inside comp.object. Just today, while responding to Bryce Jacobs, a better way of describing what we're doing came to me. It's buried in:
In fact, after inspecting multiple C APIs for LDAP, iCAL, and other libraries it appears it's not even foreign to C programmers. Often a structure is used to hold state information for a connection to a resources, but the format of that information isn't exposed to API-users except through an API. Even when a method may only be returning a value from the structure, API programmers know that level of indirection affords them flexibility inside the structure they may use without negatively impacting all the API's users.
So a common pattern is used by both C and OO programmers. What my paper is promoting (and I'll try to do a better job explaining) is that the same pattern be applied to how OO programs access the DB.
Essentially, my paper on transaction processing encourages thinking of the database as one big object with all the rules for data hiding and interfaces OO programmers are already acquainted with.
Why shouldn't applications have embedded SQL? Because it's the same as accessing the private data members of an object. It shouldn't be done. OO programmers know the correct way to interface with an object is to use its method interface--not attempt direct manipulation of the object's data. OO programmer's attempts to violate that rule is what causes so much frustration mapping the application's data graph into a relational database's tables, rows, and columns. Those things belong to the DB--not to the application.
Now, OO programmers and system designers can return to their favorite Patterns books and reevaluate the lessons from a new perspective. Should make some interesting reading.
I know, I know. It's spelled T-A-O.
I'm willing to go out on a limb and say most new programming in the last 15 years has used object-oriented languages and relational databases. I hear complaints already. True, there are exceptions, but however big you think they are they're only rounding errors compared to the military-industrial complex which has become the object-relational mega market featuring Java, C#, PHP, Python and other OO programming languages.
I'll go out on another limb (while I'm here) and claim most of those systems are horribly designed. I know that because a) they didn't ask for my help and b) everyone's complaining the offspring of OO/RDB shotgun weddings have contorted features. Design patterns for persistence and object hibernation have become more most-popular-kludges and less best-practice how-tos.
What is a transaction?
For us, a transaction is anything that might happen in our system for which we want to provide security and an audit trail. Everything that changes our system is recorded with a user name, a program name, a post date, an effective date, and a transaction type. Financial transactions include amounts and account numbers. Data changes the object and field changed, and the old and new values. Right or wrong, whatever happens in our system leaves a trail.
That trail provides two important features. First, no matter what happens to the system we know how it got that way. If an error is made we know how it happened and how to correct it. It's important to understand we don't back out transactions. We add new transactions reversing the negative (incorrect) affects of the errors. Pretending bad things didn't happen by backing-out transactions and resetting the data to pre-mistake values doesn't make them go away. Erasing data only creates an opportunity for the database to get out-of-sync with the audit trail and weakens the system's integrity if the popular mechanism for correcting mistakes is erasing the evidence.
In the movie Clear and Present Danger, the President of the United States is worried the press will discover one of his best friends was laundering money for a Columbian drug cartel. Presidential advisers recommend putting distance between the president and his murdered friend. Responding to the suggestions the press won't find out, the president responds saying, “They will. They always do.” CIA analyst Jack Ryan (Harrison Ford) recommends the opposite approach saying, “There's no sense defusing a bomb after it's already gone off.”
Always forward, never backward.
The second major feature is the ability to tell users (and auditors) what the system looked like at any point in time. It's easy to tell users what it looks like now, but what about last month? Or the second quarter last year? Or how about comparing this year-to-date with last-year-to-date? Transactions make that possible.
Related to that second feature is the fact that however the system appears today it is merely the end result of all the transactions posted since our current system bootstrapped October 1, 2002. Most of the database can be recreated by reposting transaction history. For example, today we could lose the entire account table's contents and derive the correct ending balances from history.
Database integrity is paramount and transaction history is an important ingredient. If the database is incorrect or its integrity lost, everything else is cosmetic.
More on transactions later.
Wagging the database
If the database and its integrity are so important, when should its design be influenced by programming language? In a word, never. To believe otherwise suggests a database' design should change whenever the application programming language changes and that applications written in other languages can not be properly accommodated with a database designed for another. Conceiving of that kind of dependence is counter-axiomatic to tenants OO designers strive for: high cohesion within a module and low coupling between modules. Making database persistence an innate responsibility to every business object destroys each object's cohesion and tightly couples those same objects to something they shouldn't be co-dependent with—a database. The result is objects with low-cohesion and tight-coupling. To make matters worse, the contagion isn't another object in the OOPL that can be easily coded-away but a remote object that throws exceptions, is often network-remote, and is affected by many more external influences to design and performance than the objects attempting to integrate its utility with their persistence models.
“.. like another hole in the head”
In our own system, the database is just as easily accessed from PHP and Smalltalk as it is from Sybase's isql or the open-source is (written in C). Even were the database inclined to favor object oriented languages like PHP and Smalltalk it must still treat other paradigms equitably. I can think of few other languages as far apart in paradigm as Smalltalk and C.
Think of it as a due-process clause protecting the rights of applications no matter their language, paradigm, compilation, interpretation, or generation, or OS origins.
It's difficult to imagine all these products, frameworks, and seminars for something we want to pretend doesn't exist—the database. We don't need consultants—we need therapy.
The first step of whatever 12-step program launches the next beverage revolution and meets Wednesday nights is to admit our denial. It's not that we don't know the database exists—it's that we want it to disappear. We want it to go away. When we rub our eyes hard, in-between the kaleidescope colors we imagine pure object databases undetectable to the untrained pointy-haired manager or offshore programmer. But when our eyes open we're reminded how horribly awkward, non-standard, and even more difficult-to-share-than-query object-oriented databases are (and wait 'til you see the billing rates those consultants get).
What does it look like?
Everything InStream does, from production support all the way down to development follows a paradigm, if you will, of transaction oriented processing. It's a way of looking at the purpose of your database and how programs interact with it that nullifies any impulse programmers may have to attempt to make the database reflect OO models or OO models reflecting the DB design.
After the database has been designed and independent of whatever language will predominate application programming, stored procedures are created for adding, changing, and querying the database. With those procedures in place scripts can easily be written to populate the database with data so that tests can prove the database is complete and properly designed. It is important to begin exercising the database early in a development process to discover any relational awkwardness and to establish performance baselines for both data changes and queries.
Using database stored procedures also insulates application code from database design changes from the trivial (renaming a column) to the extreme (redesigned tables). Procedures form the first and lowest-level Application Programming Interface (API) of a layered system. Additionally, most relational database provide mechanisms for finding inter-dependencies within the database, like which views depend on which tables, which tables contain which columns, and which procedures depend on which database objects, as well as dependencies between the procedures themselves.
HINT: Don't embed SQL. Once in production you want to minimize the impact of any post-production changes. You only release a system once. Everything after that is an update. The more difficult updates are to deploy the more risk is involved attempting them which may lead to reluctance tackling them and the more traumatic they become. Keep 'em simple and your system will be able to grow without stretching your customers patience.
In July of 2004, I wrote on my webpage:
I had an epiphany today.
It started a few days ago during a thread storm in comp.lang.smalltalk (cross-posted to comp.object comp.programming) discussing the virtues of statically typed and dynamically typed languages. The final puzzle piece appeared in a posting suggesting the semantics of OO programming required static checking. As I thought about it I came to quite the opposite conclusion. The gist of OO analysis and design are commonly boiled down to two goals, high cohesion within a module and low coupling between modules.
Those premises are generally accepted in the OO community, but static typing actually works in opposition to low coupling by erecting multiple requirements (contracts) between modules so that it can be mostly verified at compile-time that an exception won't occur at runtime.
I think I can boil my comments down to a few statements:
- It is not the responsibility of the receiver to determine the qualities of the sender, but the privilege of the sender to determine the suitability of the receiver.
- The value of a program is realized in its execution, not its construction. So the emphasis for its design should be on adaptability and extension, not predictability and confinement.
I'll expound more on this another time. It's already 2AM and sleep might help me workout more examples of how this premise can either be strengthened or torn apart.
In February 2005 I added:
A little while ago I discussed an epiphany I had about static typing increasing the coupling between modules, and how that is a "bad thing." There's a recent thread in comp.object discussing this very thing, Creeping Coupling.
Yes, that was 2004 and 2005.
Today I read an interesting post in comp.lang.smalltalk alerting devotees of Smalltalk, Ruby, and Self are finding new converts to late-binding languages.
In the newsgroup post, Helge Nowack includes a link to an IBM paper, written by Bruce Tate, describing his discovery of the liberating world of dynamically typed languages that reminded me of Bruce Eckel's conversion after he discovered Python (must be something in the name, Bruce).
Rereading that post and one of my own took me back to 2002. I was looking for a webpage introducing a debate between Richard Gabriel and Guy Steel titled, "Objects Have Failed" (my gratitude to The Way Back Machine for preserving a webpage that has since disappeared).
Reinvigorating the debate of dynamic vs. static languages is important to me. As a startup, our company's intellectual property largely expressed in our software. I knew from experience using other programming languages that the solutions designers imagine are heavily influenced by languages they use to think about them. The elegance of a solution, for experienced programmers, is directly proportional to the elegance of the language that solution is implemented with.
Our choice of languages was not coincident with the flavor de jour or the product of previous familiarity. It was a deliberate decision to improve the system's design by improving the language we used to design with. It's difficult to conceive a recursive solution if your vocabulary doesn't include the word recurse, or iterating through collections with closures if your vocabulary doesn't include collections or lambda expressions. This is not unlike the difficulty modeling the physics of our universe without Newton's Principia Mathematica.
Language can either widen or narrow your thinking. The more limited or incongruent your language the more limited and incongruent your solutions.
In the OO community there is a lot of talk about refactoring. For anyone in a self-imposed news blackout, refactoring is a process programmers use to simplify source code without altering the functionality of the application. One of the challenges to successfully refactoring is insuring the application's features haven't been changed. To address that concern many development shops have embraced a parallel process called test-driven-development (TDD). TDD builds test cases for each of an application's features. When the test is passed it can be documented that the feature is complete and the programmer is finished.
Recently our development team did some refactoring on a different scale than application features--we actually refactored the features. Over time applications can accumulate features that after a brief moment of euphoria are abandoned by users. These would be features that sounded good at the time they were requested but were later discovered to be unnecessary for a variety of reasons.
Regardless the reason, each application feature carries with it baggage that must be hauled around in until the application is retired. That baggage includes menu items, user interface, screen real estate, documentation, source code, unit tests, system tests, regression tests, audits, security, memory, and other items that when collected together prohibit its fitting in the over head compartment for short flights.
For production software, there are no short trips and there's no such thing as fanny-pack features.
So our recent (and successful) large scale refactoring had to do with reviewing which of our application's features had been unused (or little used) and confirming our data with users, which lead to the elimination of some orphaned features. With the features removed we obsoleted the code that implemented them , the tests that checked them, and the result was an application which is even easier to maintain and enhance because its bloat has been remedied and in the future we'll have more time to address newer and more useful (and used) features because we won't be spending time on the unused ones.
If you've done this already what were your results? If you haven't or are unable to, why is that?
In the six years our company has been around, the technological landscape our system was developed for changed drammatically. Additionally, the way our system is assembled and the components it's assembled with have also changed. Can a system go through that much change and still be consistent with an architecture described six years ago?
Yes.
Rules, as I've said in earlier postings, are more durable than technology. That is why rules are important. Design rules persist across technological smokestacks. Good rules are good rules regardless which database you use, regardless which language you develop in, regardless whether your application exists on the web, is client server, or exists only in the back office.
I won't give-up all the secret ingredients, but here are some of our favorites:
It's better to be explicit than implicit
The code systems are built with, regardless of programming language, aren't susceptible to the same fragile memories humans suffer. Code never forgets, but humans do. Has that ever happened to you? It happened to me. Considering how complicated sofwtare systems are and how many distractions humans are challenged with on a day-to-day basis having code that distributes its implementation across classes, procedures, and triggers doesn't help programmers trying to understand how something works and actually erects barriers to its enhancement. This is one of the reasons our system has only a single database trigger. Want to know what a procedure does? Everything it does is right inside the procedure's code.
If something's broken, I don't want to be hunting around forever tracking it down.
No Polling
Besides wasting CPU it's a cop-out to a work-flow problem. There's no excuse for not knowing when the next thing needs to be done.
No Parsing
We don't write compilers or invent languages. We're surrounded by technology that already knows how to parse grammars. Find the right tool and use it. There are better ways to know what's coming next.
Once and only once
I hate solving problems twice. Keeping both sections of code in-sync with each other and the business is like making sure twins both get the same amount of ice cream. By the time I've measured it perfectly it's already melted. If seems like it needs to be solved in two different places chances are it should be moved somewhere else and solved once.
No harm running twice
Humans are fallible. Everyone knows it. Why write programs, scripts, fixes, or patches that depend on being run under perfect conditions? Fix scripts should make sure things are still broken before they try to fix something that isn't. Programs should know not to do anything if there isn't anything to do. Nearly everything we've written can be run as many times as people feel like running it without negative consequences. There are enough things that can go wrong that aren't under our control--let's not add to them with things we do control.
Do one thing and do it well
Structured programming, refactoring, and the Unix shells have demonstrated how powerful a concept it is to do one thing and do it well. Narrowing the utility of a program reduces side effects and increases its utility. When all a system's programs do only one thing there is less chance of overlap and duplication. It also means that when something goes wrong there are fewer programs that require fixing.
Desireable undefined behavior
What happens when an unidentified transaction arrives? What happens when something occurs that wasn't planned for and stopping the system is an unacceptable option? You make sure there's default behavior that does something unharmful, auditable, and alert the authorities.
One of the constraints of our system is to be able to import supply chain data from multiple external systems we don't control (see Database Rules). Because 3rd-party data is sometimes incomplete our systems need to do something reasonable with imperfect data, especially since the alternative risks the database's integrity.
In production, it's important to know that when bugs appear the likelihood of something bad happening is minimized because there's appropriate default bahaviors. We know where our system is most at-risk and write our software with unharmful fail-safes.
These are some of our rules. At times we've been tempted to break them, but found ways to stick to them. As I wrote in the first Rules to Live By article, when multiple programmers and designers are working with little supervision, its best if they all know what the rules are so no one, especially me, is surprised with the implementation--except in a good way.
It's in our nature?
In The Aberdeen Group's Supply Chain Finance Benchmark Report, 38% of buyers and 36% of suppliers reported the cost of or strained resources of IT as challenges to Supply Chain Finance (SCF) programs.
Perhaps another reason its hard is because sharing data with 3rd parties might expose how our IT house isn't in order.
Recently I was talking to a company with plants scattered hither and yon that waited until a few days before a payment was due to even bother creating a payable. Each plant had its own system and most of them didn't bother fordwarding invoices to headquarters until it was almost too late. In most cases this wouldn't be tolerated since the treasurer wouldn't be able to accurately forecast their cash needs. As it was this company's sales have been flat to declining so the cash needs were similarly flat to declining.
If IT knew nothing about what a 3rd party may need for a supply chain finance program what do you think they'd need? How difficult can it be?
It's easier than you think.
We need to know who your suppliers are.
We need to know what you owe them.
We need to know how frequently you pay them.
That's basically it. A little bit of history is nice. That can be exported.
You do not need to create a website. SCF vendors already have them. In fact, it's better if you don't have a website because those are difficult to extract data from without fragile automated robots. Remember, we're not just indexing words from you website. We're accessing secure data with credentials that needs to add up.
You don't need to wait for a three-way match. Some SCF vendors already do that. It's how they measure risk. You don't need to do their work for them.
You don't need to guarantee payment. Some SCF programs require buyers to guarantee payment, but that doens't offload much risk, does it? Why replace trade payables with debt on your balance sheet? We didn't think so either.
So what of all the automation? It's not complicated stuff. Buyer's run jobs to export data. They may either post it to our systems or we'll fetch it from theirs. Whichever is easier.
What of the file formats? We're flexible. We just need the data to be accurate. Payments should pay whatever the data said they were going to pay. What gets paid should have something in common with what was shipped, like an ASN number, packing slip, invoice number, receipt number, whatever. Let us take care of the rest.
I love this quote:
The key to Best in Class companies’ success is they have taken an automation approach and an SCF portfolio approach that lets them achieve extended payment terms while providing benefits back to the supplier.
What if being best-in-class didn't cost any money or wasn't particularly hard? That is, what if someone else did all the work because they had an independent profit motive that only required your cooperation while you take all the credit for extending supplier terms, providing payment and reconciliation transparency, and enabling receipt, invoice, or invoice-line-level finance for all your suppliers? Not a bad deal.
Perhaps what best-in-class companies are best at is taking credit for something others did? It always looks easier when professionals do it.
In September 2003, Wired Magazine reported that MIT had taken a Nobel-prize deserving step by making all its lectures available online for anyone around the world. The article, MIT Everywhere reported:
When MIT announced to the world in April 2001 that it would be posting the content of some 2,000 classes on the Web, it hoped the program - dubbed OpenCourseWare - would spur a worldwide movement among educators to share knowledge and improve teaching methods. No institution of higher learning had ever proposed anything as revolutionary, or as daunting. MIT would make everything, from video lectures and class notes to tests and course outlines, available to any joker with a browser. The academic world was shocked by MIT's audacity - and skeptical of the experiment. At a time when most enterprises were racing to profit from the Internet and universities were peddling every conceivable variant of distance learning, here was the pinnacle of technology and science education ready to give it away. Not the degrees, which now cost about $41,000 a year, but the content. No registration required.
MIT earned the distinction as the only university forward-thinking enough to open-source itself.
Now, three years after Wired's article and five years after MIT's announcement, Yale announces it will be the first to offer free video lectures. ComputerWorld reported:
The 18-month pilot project will provide videos, syllabuses and transcripts for seven courses beginning in the 2007 academic year. They include "Introduction to the Old Testament," "Fundamentals of Physics" and "Introduction to Political Philosophy."
This is great news. I may just have to take a peak into that Political Philosophy class..
eWeek recently published 10 Languages to learn, an article discussing 10 technologies (most of them computer languages) experts advise programmers should become fluent in to improve their employability.
ComputerWorld blogger Martin Brown points out two of the 10 aren't even languages, which I admit I missed, but he basically approves of the list.
There are multiple reasons to learn a new language and employability is near the top if not #1. Assuming that's taken care of another reason is to learn how to think differently. If every language you learn is Algol-based (C, C++, C#, Java, PHP) then the article is correct--languages are mostly alike except for syntax.
But some languages actually change the way you think.
Bruce Eckel, author of the books Thinking in Java and Thinking in C++, had an epiphany of sorts after learning Python. In his May 2003 blog article, Strong Typeing vs Strong Testing he commented:
It's interesting that it takes an earth-shaking experience like ... learning a different kind of language to cause a re-evaluation of beliefs.
In the early 90s I had played with C++ and Objective-C but was unsatisfied either language would help me think using objects (as in Object Oriented Programming) since both languages made it too easy to slip backwards into a traditional C-way of solving problems. At that time Smalltalk was the most purely object-oriented language available. I think it still may be. Using it would make sliding into my C-omfort zone nearly impossible. It worked. Now I know what OO programming looks like and how common and not-so-common programming tasks are solved using objects. I can tell you it doesn't look like Java.
Smalltalk should be on your top-10 list of languages to learn because it will teach you how objects are used to solve problems and knowing that will make you a better OO programmer regardless which OO language you use.
Another language that should be on your top-10 list is LISP or any of its derivative languages like Scheme. Paul Graham does a better job of advocating for Lisp than I could do here, and toward that goal I recommend reading his essay, Revenge of the nerds.
Lisp should be on your top-10 list of languages to learn because it will teach you how lists and recursion are used to solve problems and knowing that will make you a better (though perhaps unhappier) programmer regardless which language you choose.
In both cases, I've not discovered a Lisp or Smalltalk programmer pining for Java or VB, though I've found and read many Java programmers wishing they could using either Lisp or Smalltalk.
"I believe that many of the systems we build today in Java would be better built in Smalltalk and Gemstone."
--Martin Fowler, JAOO 2003
In today's Great Lakes IT Report (everyone should read it every AM) the article, Michigan Tech scientists help decode cottonwood genome was particularly interesting. This part especially:
As evidence of global warming mounts, scientists are studying trees as an alternative energy source. Like fossil fuels, they release the greenhouse gas carbon dioxide when they burn. However, unlike oil and gas, they absorb it from the atmosphere as they grow, essentially mopping up after themselves.
I think it sounds like a great idea if the it's true the amount of CO2 released through combustion is the same amount absorbed during growth. If only 20% were recycled I'd still think it was a good idea.
But I remember in grade-school science I was taught all the world's oil, coal, and natural gas reserves are the by-product of plants. Sure, they were plants that died millions of years ago, but essentially the carbon dioxide we release into the atmosphere burning fossil fuels, if we follow the logic of Michigan Tech's scientist, was in the atmosphere to begin with. Essentially, today's plants and trees, even Tech's cottonwoods, may become oil, coal, and natural gas in a few million years for future generations to put into their lawn mowers (if lawns exist a million years from now).
InformationWeek editor Tom Smith is partially justified complaining about goverment's wasted money in his blog article, Bungling Bureaucrats And IT Debacles--but only partially.
He chides the FBI for wasting $170 million on its abandoned Virtual Case File system and the IRS for $20 million orphaned project and an estimated $318 million in potentially incorrect refunds, but he's not being honest with himself and if any of us in private business are mad at government we would do well to remember our own failures.
According to studies (it depends on which study) between 53% and 70% of all projects end in failure of one kind or another. Chances are the larger the company is the more frequent and costly those failures may be. Considering the size of government Mr. Smith's examples seem pale compared to some of the failures we read about regularly from other industries.
In 2004 Information Week reported that Ford Motor Company cancelled its Everest project with an estimated $400 million in sunk cost. By comparison both the FBI and the IRS have done better.
What Mr. Smith also fails to appreciate is the courage it takes to walk away from a project that's not going well. I think the FBI and IRS should be congratulated for walking away when they did before the tab got any higher.
This is the third installment of our "Rules to Live By" series. The first entry was Everything you needed to know about software architecture you learned in kindergarten. It discusses the importance of sharing a vision with coworkers about where the system is going, the metaphor you're using to model after, and the rules it must follow.
The second installment, Production rules easy as 1, 2, 3--but without 2 and 3, talks about our production priorities, and how those priorities are reflected throughout development, testing, and assurance. It also talks about how knowing our priorities and what's important allows us to upgrade our system more aggressively and frequently than is possible for other software teams. The result is to deliver more features and correct problems as quickly as possible for both our users and ourselves.
This article is about our database design. It's intended for software designers and not DBAs. There's nothing here about relational theory, 3rd normal form, or performance tuning. The only thing a DBA may find interesting about our system is that the logical and physical designs are the same. No compromises are made translating our logical design's entities or relationships into real tables. Nothing was denormalized. It was myth in the 90s and still is today, even on more powerful equipment, that normalized data doesn't perform.
The basics
It should go without saying, but won't, that the first step to good database design is understanding your business. Sometimes that understanding comes from interviewing "experts." Our development staff has made it their goal to become experts in our niche of commercial finance so our interviews have a more consultative feel than an interrogation.
Fundamentally, we assume solid database design skills. Consistent table naming makes it easy to predict where data may be stored. Strict attribute naming requirements remove confusion about what a field's meaning is: there are never two meanings--fields are never reused.
Some of our rules may be specific to our industry, but I'm wary of provincialism. Believing we're unique would discourage us from both looking outside ourselves for ideas and from publishing what we've discovered improves our processes.
What's yours is yours
Our system tracks documents exchanged between trading partners. To maintain the system's integrity we never add to, change, or remove these documents if they exist "in the real world" and have been recorded in ours.
Furthermore we never create documents we don't know exist, even if it would make sense that they did. Suppliers don't normally ship without a purchase order and it's easy-enough to create a PO if one is obviously missing. But if we created the purchase order without evidence it exists then we've compromised the system's integrity. Better to be missing a document than to fabricate one.
Real world integrity is more important to our business than relational integrity. Besides, there are other ways to synthesize relational integrity to support these situations.
What's ours is ours
Rather than pollute real-world documents with our data we keep them separate. Our system maintains proxies for customer's documents to which we add our own fields. This separation-of-stuff provides our system with a kind of flexibility otherwise unavailable. We're able to mold and bend our system any number of ways behind the scenes without impacting our customers' view of the world.
Other duties as assigned
Never reduce resolution. One we've created or captured detail information we won't delete or summarize it away. We violated this rule once and haven't forgiven ourselves since (and yes, we knew it was a rule then).
Our code may be object oriented (Smalltalk and PHP) but our database is not. Forcing one to look like the other is a disaster waiting to happen--at least it is in financial systems. Resist the urge to treat your relational database as if it were an object database or to impose your code's object model onto your database. We use an approach we call Transaction Oriented Processing to marry the two. RDBs are from Mars. OO is from Venus. Transaction Oriented Processing is the medium between the two. There is no object-relational impedance mismatch if we let both systems do what they're best at. Using Transaction Oriented Processing to negotiate between OO and RDB technologies is straightforward but requires a separate article to explain. I promise to post one here.
It is better to be explicit than implicit. Nothing in our system is subtle. There are no hints, innuendo, or clues. Intuition is not necessary to find out what's going on. Everything is exactly what it says it is.
For instance, there is only a single trigger in the entire system. If a programmer wants to know what happens when a row is inserted or changed they need look no further than the stored procedure that inserts or changes the row.
You're going to be married to your database for a long time. It's best you learn how to get along with it.
:: Next Page >>
Anything worth doing is worth doing poorly. These were the words of a senior bank executive known for getting things done, getting them done fast, and improving them with time. While others waited for Godot he made money.
Thomas Gagne, the author, is the CTO of InStream Financial.
| Next >
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
| << < | ||||||
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | 31 | ||