This month's column is the day-to-day log of a software-development project that I assigned myself in March.
Why am I inflicting this on you? This is not an example of fine-and-fastidious programming. It's not even an example of quick-and-dirty programming. It's not really programming at all. What it is is software development by wiring up off-the-shelf objects, which, if memory serves, was the promise of object-oriented programming.
But despite the claims of OOP boosters, the world has not seen a lot of software development by wiring up off-the-shelf objects. Perhaps when you read this column you'll know why, but I hope you'll get something else out of it. My goal is to show, in a detailed case study, exactly what a weekend programmer can do with off-the-shelf objects and a tight deadline.
In March I decided I needed to write a BBS for subscribers to my HyperTalk newsletter. When I considered HyperPub's publishing schedule, my own work schedule, and various other constraints, I concluded that I needed to do it in three days.
That is, I wanted something that I could start shaking down in three days.
I was willing to settle, in this shakedown version, for the simplest possible BBS interface: one-character mnemonic items in a hierarchical, command-line menu system. At the top level, the user could type F to go to a Files area, M to a Mail area, and N to a News area; at lower levels, typing a numeral should select among files, news items, or mail messages. Typing H at any level should display context-sensitive help text; typing X at any level should exit that level, logging off at the top level. The system should respond with appropriate welcome messages, prompts that indicate the available options at any point, and a logoff message telling the time online and the logoff time. I also wanted the logon to be tied to our customer database, so that subscribers to HyperPub would be let in and anyone else would be locked out, but that feature could wait.
A major purpose of the BBS would be file downloading and uploading, and I didn't want to reinvent that wheel; I wanted a module that I could drop in to handle file transfer. I was willing to settle initially for a very simple mail system that would let users read mail addressed to them, type in messages online, and send them to the sysop, other users, or all users. I was particularly interested in creating a news system, a sort of customized and curtailed NewsBytes service, in which I would supply the news items.
That's where I stood at the beginning of day one. Here's my log of those three days.
The first decision I have to make is what tool I'm going to use.
Although I could buy a BBS package for under $100.00, I want to get some knowledge of BBS design out of this project, and I also want the ability to tweak the system later on. The time frame and my abilities, though, limit me to high-level tools: I won't be writing this from scratch in C. And I'll be running the BBS on a Mac, so that narrows my options further.
I'm familiar enough with HyperTalk, and all the necessary tools do exist in that environment, but I doubt my ability to tie them together. I've done some work using Serius Developer, and it has an impressive-looking communications library, so I'm drawn to it. And Serius will let me write my own objects for future tweaking, although for now I can just hook up supplied objects.
At least that's the theory. I check the documentation to see if Serius has what I need, and it looks promising. Serius delivers several specialized object libraries, including a communications library with three communications objects for managing file transfer and so forth. But there's only so much I can learn from the docs, so I prepare to install the product.
First I have to set up the hardware, which takes way too long and casts a pall over the whole project. If it takes me an hour and a half to figure out what's wrong with the internal modem in one of the machines, what hope do I have of getting this whole project done in three days? I'm talking HayesSpeak before I'm finished (that Atlanta dialect in which every word starts with AT). Almost as much fun is the session under the desk, figuring out what I can unplug so that I can have two machines next to each other and still plug in the external modem that one of them requires.
After this, the software installation, including the various necessary components of system software, is a treat.
The communications package includes a sample communications application. My hope is to use as much of this as I can, so I launch it to see what it does.
The sample application uses all three of the communications objects that come in the Serius communications library: a connection object, which manages connections on various channels, a terminal object, which lets the machine emulate a terminal, and a file-transfer object, which, I hope, will manage file transfers for me.
The connection object has functions for opening a communication channel and waiting for text to come in, which is what a BBS spends much of its time doing. Its capabilities are invoked in the sample application by menu choices, so I'll need to change that.
By the time I figure I know what this sample application is doing, I've used up half my first day.
After lunch I examine the code--although code is not the right word. Serius is a visual programming system, and at the level I'm using it, it's a tool for wiring up predefined objects.
What these two facts mean in practice is that you "write" programs by dragging icons around the screen. Serius has a scripting system, though, called ObjectTalk, and I print out the ObjectTalk version of the sample application to study it offline. I immediately regret doing so. I have forgotten that ObjectTalk is just an alternative way to supply parameters to functions. The ObjectTalk printout is useful as documentation, but I find it useless for studying the program's design. This is a visual programming system, through and through. I go back to the computer.
Like any GUI application, this one has a lot of functionality tied up in UI stuff: maintaining menus, closing or opening windows, and so on. This part is easy to follow.
I get confused, though, trying to see what the connection object does with incoming text. The documentation, both online and in the manual, is a little confusing. The connection object normally passes any text that it receives to the terminal object. It can, however, intercept the text and save it in a text object. So far, clear enough. There's a perfectly clear discussion about how you can use this mechanism to buffer input, but it's not clear if I actually need to do this in order to trap my one-character user inputs. What, exactly, are the responsibilities of the connection and terminal objects regarding this incoming text? The documentation is not clear.
This is a familiar problem for anyone who has constructed any hardware system from components. At the interface, nobody wants to take responsibility. But Serius is the supplier of both components. I need to understand how this works, since recognizing and responding to user inputs without missing a byte is essential to the proper functioning of the BBS. The sample application doesn't do anything with incoming text, so it's no help either. The documentation and the sample application having failed me, I resort to thinking. This desperate tactic works, and I resolve that problem and go on to the next.
I end the day in thinking, without having created any code. I go over each component of my BBS, trying to anticipate problems. I anticipate none. Piece of cake.
I spend the morning modifying the sample application to see if it will do what I need. The connection object works the way I reasoned it should yesterday. It provides me with a configuration dialog, which is how one controls what it does. Here I can specify text strings I want it to detect in the data stream from the modem and signals I want it to send when it detects them. That's all I need to do with the connection object itself. I'll then associate function chains with each of these signals to implement the behavior each user input string is supposed to evoke.
Here's what I actually do to accomplish this:
It is also messy, but I assume that's just because I'm not properly modularizing this design. The screen is getting awfully cluttered awfully fast, though. I wonder how small I'll have to make my modules in order to make them understandable.
Serius compiles a program of this size pretty fast, so I am not tempted to one of my other programming sins--a tendency to write too much between tests.
By noon I have a skeleton of a BBS running. I can dial up, and it greets me, explains the commands and menu hierarchy, responds in some way to each command I type, and lets me upload or download a file. One specific file. When I type X it sends me a logoff message, breaks the connection, and hangs. Oops. Apparently I get one logon per restart. Well, it's something.
After lunch I start from scratch, sort of, building a new application and pulling in pieces of the sample application (and most of its structure). This time I try to modularize properly.
Writing modular applications using. Serius means using subjects. Serius calls an application under development a "project," and modules of a project "subjects;" that is, sub(pro)jects. Each subject has its own work window, and objects can be shared among subjects.
I create subjects to handle logon and logoff, the Files area (uploading and downloading), the Mail area (messages between users or between users and sysop), the News area (displaying brief news items), Help (context-sensitive), and a User list display (which eventually will be fairly complicated because of the need to get data from our existing customer database).
As I drag and drop function icons, I can see that this is the right level of modularity. The windows are not too cluttered.
I am now considering a question that I dodged yesterday. The sample application lets the user set the parameters for the connection object by invoking the standard dialog for that object. Since this isn't programming, there is no API--there's an "application configurer's interface," which in this case is a dialog box. But the connection object controls more communications devices and channels than I am interested in, and there's no way to turn off part of this configuration dialog. Unless there's another way to set connection object parameters, I have the choice of providing the sysop with either too much or too little control.
It turns out, after I dig into the documentation, that it is possible to get the entire connection object configuration as text and to set any part of it, so I can in fact craft my own dialog for setting exactly the parameters I want the sysop to deal with. Later for that.
This one has turned out not to be a problem, but it is the sort of thing one worries about in high-level programming: When the tools do a lot for you, they may not do exactly what you want. So far, I haven't had that problem with Serius.
By the end of the day, I have this second version hobbling along. It gives the user a choice of files to download and news items to read, resets itself so it can answer the phone again after a proper logoff or any broken connection, and responds to all user commands, if not always correctly. The user list is bogus (I haven't begun to think about how to link into my customer database), and help is still noncontextual. Also, I haven't got the mail system running yet.
But it's forgotten how to download files. The problem seems to be that I'm not properly supplying a parameter to one of the functions. This should be easy to check, but the Serius documentation is a little vague regarding parameters, supplying neither an unambiguous BNF-type specification nor examples. I think the function is dissatisfied with the pathname I'm giving it, but it sure looks like a pathname to me. Grumpf.
This is it: the final day. I'd better prioritize. What do I absolutely have to get done today? Fix the pathname bug, for sure. I brute-force my way to a resolution of that one.
I can't worry now about baud rate. The whole world is not 1200 baud, but the part of it that wants to communicate with this BBS had better be.
Next I clean up the user interface, especially the Help system. Making it contextual takes about half an hour: 15 minutes to change the signals and add the text objects, and 15 minutes to type in the text of the contextual help messages.
I've been using dummy files to download in the Files area and dummy news items for the News area, so I replace these with the actual data I want to make available to my users. It's clear to me that I'm not going to get the Mail area implemented today, so I punt that.
I spend a couple of hours tidying up the icons and adding comments so I'll understand what I did when I come back to this thing. Figuring I've got enough time for one more tweak, I add the time online and the logoff time to the logoff message. Doing this requires that I quit Serius in order to add the time object to my project. Actually adding the times to the logoff message takes all of two minutes.
At 6:00 I take the BBS online and invite my beta testers to have at it.
Since that three-day crunch, I've done more work on the BBS. I chickened out on trying to connect it directly with our customer database, opting to just read in a TDF file of subscribers exported periodically from the database.
Getting the Mail area working only took one more day, although it's still rudimentary. It doesn't handle replies, threads, or offline message composing. It's clear, though, that none of these things will strain Serius when I get another day or two free to add them.
What did I learn? A fair amount about building a BBS, although nothing of any depth about file transfer or managing a communication channel. Building an application from existing objects is pretty simple when one supplier provides all the objects and some of the objects represent big chunks of the task.
Oh, and I'd better keep my day job.
Copyright © 1993, Dr. Dobb's JournalThe Project
Day 1: Assessing Feasibility
Day 2: Getting Something Running
This is visual programming. It's easy.Day 3: Facing Reality
Lessons