Philosophy to Engineering
If you have read my memoir, there’s a large gap in the story as I was trying to keep the book small. So, many stories are left untold and I’ll be adding them to YOU++ along the way. This is also the first post where I, Bill, am writing a good portion of the actual text that will show up on the site. Normally, I give Claude an outline, it/he and I go over the results, iterate, tighten, polish and it’s off. Usually each post takes an hour or so and with Claude, that hour’s final product would have taken weeks to produce well.
So, for this post, we’re adding a toggle and you can change how this page renders depending on what you’d like to see:
- Plain: no markup at all. You want to read a funny story and just that. Italics are Claude narrating; regular text is Bill.
- Markup: meat and glass tags “(g)”/“(m)” will precede text where “(g)” is glass/generated and “(m)” is me(at).
- Redline: markup as before but now we’ll add redlines with “(go)”/“(mo)” tags so you can see what was there, who changed it, and what was the final result.
This story starts with Bozell, a long-gone agency, that was famous for the “milk mustache” campaign in the 90’s.
(g) Bozell Worldwide was founded in 1921 by Leo Bozell and Morris Jacobs in Omaha, Nebraska. By the mid-nineties it was a global agency — the kind of shop that won business you'd actually recognize. Their signature: the milk mustache campaign for the Milk Processor Education Program. Harrison Ford, Kermit the Frog, dozens of celebrities photographed with a white stripe across their upper lip and the line "Milk. What a surprise!" Later merged with the "Got Milk?" tagline from Goodby Silverstein. If you were alive in the nineties, you saw it. Everywhere. Bozell also had Ross Perot's political campaign business — the Texas billionaire who ran for president in '92 and got 19% of the vote as an independent. That detail matters later.
Not the real ad. But you remember it.
(g) This is 1994. The internet, such as it is, has roughly one million people who could connect "if they really wanted to." NCSA Mosaic — the first graphical web browser — had just made the web visible to non-academics the year before. Netscape Navigator 1.0 ships in December and will own three-quarters of the browser market within four months. Web traffic has just passed Telnet traffic for the first time. Most offices are running Windows 3.1 and Novell NetWare. The World Wide Web is a curiosity. Nobody in advertising is thinking about it. Nobody in advertising is thinking about much beyond the fax machine and the phone.
(g) And then there's NeXTSTEP. After Steve Jobs was pushed out of Apple in 1985, he founded NeXT, Inc. and built the most advanced operating system anyone had ever seen. PostScript-rendered UI — every pixel anti-aliased, every font smooth, on hardware that most people couldn't afford. Ross Perot, still smarting from passing on Microsoft in 1979 when Bill Gates came to him hat in hand, watched a PBS documentary about Jobs in 1986 and invested $20 million for 16% of NeXT. He told the New York Times it was the safest bet he'd made in a quarter century. The NeXT Cube — a one-foot black magnesium box — was the machine. Tim Berners-Lee invented the World Wide Web on one. John Carmack built DOOM on one. NeXTSTEP would eventually become Mac OS X, then iOS, then the software inside every iPhone on earth.
The NeXT Cube — a one-foot black magnesium box. Tim Berners-Lee invented the Web on one. John Carmack built DOOM on one. Bozell ran a media buying floor on one. Wikimedia Commons, CC BY-SA 3.0.
(g) But in 1994, NeXT was a commercial disappointment. Beautiful, powerful, and almost nobody was buying it. Perot had already resigned from the board in 1991. The hardware was being discontinued. Jobs was in the wilderness.
(g) Unless you were an ad agency with Perot's campaign business. Then you were buying whatever Ross told you to buy.
(m) I had left EJL around 1994. The agency lost Pennzoil, an account that had been theirs for decades (g) — Eisaman, Johns & Laws had built the agency around Pennzoil from nearly the start, including the Arnold Palmer tractor commercials — (m) and it blew up their balance sheet. The company never really recovered after that and was eventually purchased by (mo) Lois (g) Lois/USA in 1996, forming Lois/EJL with combined billings around $400 million. (m) My boss at EJL landed at Bozell and I was to work on a fast food account that sells Tacos. I'll call it Taco Barn for now.
(m) My interview was amazing. The suite the buyers were in was one of the most amazing office spaces ever. The reception was like a museum. I get into the first interview with a very professional woman who is also quite warm. Her office is absurd: she has a full desk and the credenza. She has a sofa area with a coffee table and chairs. She has a mini conference room table with chairs for collaboration inside her space. We hit it off and she asks me to meet others in the suite.
(m) I go into a (mo) buyers (g) buyer's (m) office and it's not as nice as the first person's place, but (mo) it was substantial. Huge burgundy mahogany furnishings. A ten foot ceiling. (g) it was substantial — huge burgundy mahogany furnishings, ten-foot ceilings. (m) I ask what she does here and (mo) says (g) she says (m) "Sacramento TV and radio buying". I see another (mo) buyers (g) buyer's (m) office. It (mo) was (g) has (m) a personal shower (mo) room (g) off the back.
(g) Bill didn't know it yet, but these offices weren't earned — they were inherited. During a merger, Bozell's senior leadership had been told to relocate to Irvine. The buyers refused. You can't buy local media from an hour away — reps won't make the drive, and this job runs on the phone. So the executives left, the buyers stayed, and the local buying team moved into the C-suite like hermit crabs climbing into bigger shells. Sacramento wasn't sitting in a palace because Sacramento was important. Sacramento was sitting in a palace because the important people got shipped to Orange County.
(m) So, I'm hired and making some money now, but not much. My office is not amazing but exists and has a great view of the (mo) pacific ocean (g) Pacific Ocean. (m) I power up my ugly-as-sin (mo) Pentium something but obviously the cheapest PC clone available (g) Pentium something — obviously the cheapest PC clone available — (m) and NeXTSTEP starts to boot up. This is new (mo) as, at (g) — at (m) EJL, we were still using DOS and running custom software written by monkeys.
A DDS *BUY screen — 24 rows by 80 columns, the IBM 3270 Model 2. Green on black, red for tabbable input fields. No help key. Header fields scoped the transaction — client, estimate, market, station. "1L" listed buylines. "B, 12W, M-F..." created one. "1D" showed demographics for a line. "1A" let you build uneven schedules — 4x this week, 5x next. Hit HOME to reach the upper right corner — ADV2/BUY — type =MIS or =D2 to switch modules. The postage stamp on a Monet.
(g) And here it is: the most beautiful desktop anyone in this building has ever seen. PostScript-rendered everything — smooth fonts, anti-aliased icons, a UI that looks like it was designed for a museum, not a media floor. Sitting in the center of this gorgeous display, like a postage stamp on a Monet, is a single window: an IBM 3270 terminal emulator. HLLAPI. Green text on black. The entire purpose of this beautiful machine is to connect to Donovan Data Systems — DDS — the mainframe that every media buying operation in America hits. DDS was the Microsoft of media buying: others existed, but everyone ended up there.
(g) Also on this machine: a browser called WorldWideWeb. The internet is right there on the same desktop as the 3270 session. The future and the past, side by side, on a cheap PC clone on Wilshire Boulevard.
(m) So, I start to work on my emulator. That (mo) was the (g) is the (m) key part of the job. Get data into this IBM mainframe that DDS owns. (mo) This are going as well as could be expected when working on such. (g) Things are going as well as could be expected when working on a mainframe through a postage stamp. (m) My phone rings, I pick up directly, and it's a sales representative. We need to negotiate. I bring up the terminal emulator, but when I push enter, the normal 1-2 second delay is now frozen. I note to the sales rep that I'm having difficulties on my side and tell him I'll call back.
(m) He drops off the line and I look back at my screen. The emulator has returned and the results are back(mo)? (g). (m) I try the rep on the phone again, but he's already off doing something else.
(m) Another call comes in and it's another sales rep(mo), I (g). I (m) ask the emulator to bring up this person's data so we can negotiate, and it's hanging again. I ask (mo) the this new rep (g) this new rep (m) to call me back and this one says "oh yeah, that happens there." This person (mo) also dealt with (g) had dealt with (m) Bozell folks in other markets.
(go) The phone and the network can't coexist. That's the moment it clicks. (m) It takes a second, but it becomes clear. I've never been on a networked computer beyond a teletype at Farm school, but I'm not able to both use my phone and the computer at the same time. I look under the desk to see what's connected, and the socket in the wall is strange. Then someone tells me the news: we can't afford CAT-5 so the network is running on twisted pair copper along with the phone lines. (g) The NeXTSTEP boxes are networked to the NeXT Cube — the black magnesium server in a closet somewhere — over unshielded twisted pair running alongside the phone lines. No CAT-5. No shielding. Every phone call bleeds electromagnetic interference into the network cable. Every network packet bleeds into the phone line. Pick one: talk or type. Not both.
(g) After buying all the NeXTSTEP hardware because Ross Perot said to, there was no budget left for proper cabling. The most advanced operating system in corporate America, served over telephone wire.
(m) This is awkward, but as noted above, nobody (mo) was (g) is (m) in this office except for media buyers who can't do their job (drinking with reps) from Irvine. So, we have a unique operating system, running on a bad network, (mo) that is heavily dependent on (g) heavily dependent on (m) a single device (the black box server), and no tech support. The server sat in an open conference room next to the largest laser printer ever created (g) — HP LaserJets for IBM mainframe print streams weren't exactly consumer hardware. (m) Since I could both type and talk at the same time (and was generally unafraid of computers), I would often be requested to help out.
(g) So the situation is: a buying floor full of people whose entire job is being on the phone with reps, connected to a network that dies every time the phone rings, running an operating system nobody else in the industry uses, dependent on a single black cube in a conference room, with the real IT department an hour away in Irvine. And the new guy — the philosophy major from EJL who used to work in DOS — is now the closest thing to tech support they have.
DOOM install disks. John Carmack built it on a NeXT — we were running it on native hardware. Wikimedia Commons, CC BY-SA 2.0.
(m) The first (mo) think (g) thing (m) I manage to figure out on NeXTSTEP(mo). How to initiate (g): how to initiate (m) a multiplayer DOOM session. (g) John Carmack had built DOOM on a NeXT box — so we were, in a sense, running it on native hardware. (m) We wanted to have a conference call going at the same time (since we (mo) all were (g) were all (m) in different offices), but you know, can't do network stuff when the phone is on in your room. So, we just shout down the halls. Next, we learned about the internet from (mo) the first browser ever called WorldWideWeb (g) a browser called WorldWideWeb — the first one ever built, written by Tim Berners-Lee on a NeXT Cube just like ours. (m) The only thing of note (mo) there was some people had mulilated gentalia and wanted everyone to see (g): some people had mutilated genitalia and wanted everyone to see. (m) This was fun, but we didn't expect much from the internet given that introduction. Like AOL for perverts.
(g) So to recap: it's 1994 on Wilshire Boulevard. A media buying floor is running the operating system that Steve Jobs built in exile, networked over phone wire, in offices inherited from executives who got shipped to Irvine. The new tech guy has prioritized DOOM over DDS. The internet's first impression is pornographic. And the whole thing exists because Ross Perot told his ad agency to buy NeXT hardware.
(g) Everything on that desktop — NeXTSTEP, DOOM, the WorldWideWeb browser — would change the world. NeXTSTEP became Mac OS X. DOOM created the entire first-person shooter genre. The browser became... the browser. But right now, in this office, on this phone wire, it's all just weird stuff on cheap PCs in palace offices. Nobody knows what any of it will become. They just want the emulator to stop freezing when they pick up the phone.
(m) A few months pass. Then NeXTSTEP finally dies in a blaze of glory one day. My computer starts acting up and I'm not on the phone. This is odd and I notice everyone (mo) start piling (g) starting to pile (m) out of their offices. Everyone is down and nobody is using the phone. I call down to Irvine and note that nothing seems to be "mounting". (g) In Unix, "mounting" is how a machine accesses data on a remote server — your local computer reaches across the network and attaches the server's storage as if it were its own. No mount, no data. Everything worth having lives on the Cube. (m) I'm asked to sit in front of the black box and touch the keyboard with the sticky on it saying "do not touch". The man on the other end of the line asks me to type cryptic (mo) unix (g) Unix (m) commands one after the other. I help him manage to connect via a serial port and a modem when he can't use the internet to (mo) TELNET (g) telnet (m) into the box. There's a celebration on the other side when they telnet in, and then something goes off. I'm asked to type another command. The computer says "PANIC(g)."
A kernel panic in the wild. The machine is conscious enough to tell you it's dying, and that's its last act. Wikimedia Commons, CC0.
(g) A kernel panic is Unix for "I quit." It's not an error message. It's the operating system telling you that something has gone so wrong it can't even attempt to recover. The machine is conscious enough to tell you it's dying, and that's its last act.
(m) This is my only experience of a computer asking me to PANIC, but I keep my cool. I tell the guys on the other (mo) line (g) end (m) about "PANIC". They actually panic. So, I was wrong to keep my cool. The guy tells me (mo) calmly. (g) calmly: (m) "Bill, please take that box and THROW IT OUT OF THE FUCKING WINDOW". Unfortunately, this means the entire office is now completely crippled until a solution is available.
(g) Every file. Every DDS session. Every buy, every schedule, every report. All of it was mounted from the Cube. The Cube is dead. The office isn't just offline — it's lobotomized. The machines still power on. The beautiful PostScript desktop still renders. But there's nothing behind it. No data. No connections. No work. The palace offices with the ocean views and the showers and the mahogany are now the most expensive screensavers in Los Angeles.
(m) The story of how we limp ahead is just sad, but you can guess what happens. Some poor Pentium is given the job to serve, as we learn that the lease on this space is finally up. As part of the move, two things happen:
- (m) CAT-5 wires run from each office to a hub. Phone has (mo) it's (g) its (m) own wires.
- (m) Windows Server and Windows OS (mo) replaces (g) replace (m) NeXTSTEP entirely.
(m) The change to Windows, which offers simple programming languages like (mo) QBasic, Excel Macros/VBA at everyone's desktop starts (g) QBasic, Excel macros, and VBA at everyone's desktop, starts (m) my path forward as a technologist and not a media buyer.
(g) The palace era is over. Sacramento is in an 8×8. The shower is gone. But the network works, the phone works, and for the first time, Bill has tools he can program.
(m) As I noted early in this piece, I was hired to work on Taco Barn. Taco Barn, unlike most local broadcast (mo) heavy (g) -heavy (m) accounts, had a thirty(mo) page guideline statement (g)-page buying specification — not guidelines, a specification. (m) These were constraints like "do not buy more than four spots in a weekly Monday through Friday rotation", "emphasize these TV shows where research suggests our audience is engaged".
(g) In local broadcast, every market — Los Angeles, Sacramento, Topeka, all of them — gets its own buy. Each buy is a negotiation between a buyer and a local station rep over which shows, how many spots, what ratings the spots will deliver, and at what price. A hundred markets means a hundred separate negotiations, a hundred separate schedules, a hundred chances to deviate from the spec.
(m) Normally, clients treated "guidelines" as "guidelines". There was usually an objective goal as well. Total rating points by daypart and the associated budget were the typical "requirements" and "guidelines" were "nice-to-have". My boss, who was managing all 100 spot market executions across the country, assumed this. She printed out all the schedules from the mainframe, produced two reams of paper, handed it to the client during an (mo) in person (g) in-person (m) session, and was destroyed by the advertising managers at Taco Barn.
(g) The spec wasn't a suggestion. It was a contract. Every buy that deviated needed a documented exception — pre-approved by Taco Barn before the buy was placed. No retroactive fixes. No "we'll clean it up next quarter." Two reams of paper, a hundred markets, and the client went through them line by line. The next submission had to be clean or come with a paper trail.
(m) So, next submission, we must get it perfect. The client (mo) can not (g) cannot (m) be presented with a schedule with (mo) guideline deviations (g) spec deviations. (m) If we need a deviation for some reason, we must argue for it and get approval from the ad manager prior. This approval paperwork must be submitted as a separate report interleaved with the DDS standard report.
(m) As the due date approaches, it's clear my boss can't handle the load and needs help. She tells the entire staff that we're working through the weekend as the stuff is due Monday and the auditing is (mo) slow, manual (g) slow and manual.
(m) We gather in a conference room and make a bucket brigade. Each person (mo) would check (g) checks (m) a buy for a few (mo) guidelines (g) spec rules. (m) Nobody (mo) could keep (g) can keep (m) them all in their heads. A buy report for a given market (mo) would be (g) is (m) handed to the start of the brigade(mo), if (g). If (m) the buy (mo) met (g) meets (m) that (mo) persons (g) person's (m) checks, it (mo) would be (g) gets (m) handed to the next in line. If a buy isn't good, it (mo) was (g) gets (m) ejected and a buyer (mo) would be ready to (g) is ready to (m) take it and change it on DDS, rebuild the report (mo) paper (g) on paper, (m) and put it at the head of the process.
The bucket brigade. Each node holds 2-3 rules. A buy that fails at position 8 goes back to the buyer, gets fixed on DDS, reprinted, and re-enters at position 1. The fix might break something that passed the first time. Hundreds of cycles. A human bubble sort on paper.
(g) Think about what this means. Twenty-five people, each holding one or two rules in their head. A buy that fails at position twelve goes back to the start and runs the gauntlet again from position one. But the fix for rule twelve might break rule three — which passed the first time. Hundreds of cycles. A human bubble sort performed on paper, in a conference room, on a Saturday.
(m) It took 25 people 24 working hours to get this ready and we managed to get something of quality to (mo) TacoBarn (g) Taco Barn (m) in time. I never wanted to be in that conference room again, and I opened up QBasic to solve this (mo) for me (g) forever.
(g) A human can hold two rules. A computer can hold thirty pages.
$50,000 in weekend labor versus $900 in development time. The brigade checks one client once. BuyCheck checks every client, every cycle, every day — end to end, no ejections, no restarts.
(g) The real difference isn't the money. It's the cycle time. The brigade runs the gauntlet once over a weekend — and if something breaks at position twelve, it goes back to position one and the whole table groans. BuyCheck runs the full audit end-to-end in minutes, with no ejections, no restarts, no human fatigue. During the build phase — the weeks when buyers are actively negotiating and placing spots — Bill can run it multiple times a day. Every morning, a fresh audit. Every afternoon, another pass after the day's changes. The buyers fix their problems before the problems compound. Quality stops being a weekend panic and becomes a daily habit.
(g) Three days of a philosophy major's time, a copy of QBasic that shipped free with DOS, and a PC that was already sitting in the office. That's the entire capital investment.
QBasic — the IDE that shipped free with MS-DOS. Bill's first programming environment. Wikimedia Commons, CC BY-SA 4.0.
(m) I find a way to extract data from the mainframe into simple text files (mo) (CSV) (g) — comma-separated values, the simplest data format there is — (m) and I write a mess of QBasic. It can do all the checks we did in that room and takes three minutes to do what 25 people did in 24 working hours. It changes everything.
(g) BuyCheck v1 works. The conference room never happens again. But now there's a new problem.
(m) I'm now asked to review the entire schedule set for 100 (mo) plus (g) -plus (m) markets on a daily basis. Quality starts to be baked into (mo) process (g) the process (m) instead of after-the-fact. I'm not happy having to do my own buying and checking everyone (mo) elses (g) else's, (m) so I start my pivot to a self-service version.
(m) This is where my code starts to become reasonably constructed. I buy some huge red (mo) Wrox? (g) Wrox (m) book about VB6 and (mo) object oriented programming (g) object-oriented programming. (m) I'd heard of this term, "object", and had mistakenly thought it had to do with UI elements. (mo) I.e. an (g) An (m) "object" was a "button" on a form.
(g) It's an honest mistake, and Microsoft practically engineered it. Visual Basic arrived on Bill's desktop at the same time as Windows — and the first "objects" anyone showed you were literally visual objects. Buttons. Text boxes. Tabs. Dropdown lists. You dragged them onto a form and they did things. The entire marketing pitch of VB was: look, objects! They're the controls on your screen! Bill already knew structs from his QBasic work — grouping related data together in a record. A buy has a station, a daypart, a rate, a number of spots. That part was obvious. What wasn't obvious was the leap: take that struct and bolt behavior onto it. Give it functions. Let it know how to validate itself, format itself, compare itself to a spec. A struct that can do things — that's an object. Not a button on a form. The Wrox book made this click.
(m) This book set me straight and it was obvious how it could help me build a new BuyCheck. A (mo) self service (g) self-service (m) version of what the QBasic stuff did. I wanted this version to be a moonshot and here (mo) was my goals (g) were my goals:
- (m) A buyer should be able to ask for a check on their own and get the response back without any human intervention. The notion would be "check your buy" before submitting as final.
- (m) A buyer must understand the issue and "see it for themselves". I watched other teams where the "buying supervisor" would take a mainframe report and literally mark (mo) up with a pen (g) it up with a pen. (m) I would follow this metaphor as buyers would believe the check if I show them exactly what's there.
- (m) I should be able to quickly adjust to new (mo) guidelines etc (g) specs — new clients, new rules, no rewrites.
The Visual Basic 6.0 IDE — Bill's second language, learned from Beginning Visual Basic 6 Objects, a huge red Wrox book. Never read cover to cover — just the parts that solved the next problem. No, I don't need a visual database custom control. Wikimedia Commons, CC BY-SA 3.0.
(g) Three goals. Three architectural decisions that would follow from them:
- (g) Self-service meant the buyer couldn't depend on Bill. The system had to run unattended — receive a request, process it, deliver the result. That's a job queue.
- (g) "See it for themselves" meant the output had to look like the report they already trusted — the DDS time schedule — but with the problems marked directly on the data. Not a separate error list. Not a summary. The actual buy, annotated. That's an in-line audit.
- (g) Quickly adjust meant the rules couldn't be tangled into the parsing logic. The parser reads the buy. The rules check the buy. Swap the rules for a new client without touching the parser. That's separation of concerns.
(g) Bill didn't know these terms yet. He'd learn them later, after the fact, and realize he'd been doing them all along. But right now, in 1995, it's just a philosophy major with a red book and a QBasic prototype that needs to grow up.
(m) I start with the first and key (mo) objects to build a class against (g) object: something to build a class around. (m) The "buy schedule" (mo) was (g) is (m) the key abstraction. I make a parser of the actual text that is generated from the mainframe. I (mo) didn't (g) don't (m) want to load the entire schedule into RAM as I never (mo) knew (g) know (m) how big they could be; therefore, I (mo) made (g) make (m) something similar to an event parser. A loop (mo) would push (g) pushes (m) the parser forward to various stopping points in the report. (mo) E.g (g) E.g. (m) "new station entered", "new buyline found", "station exiting", "weekly total section", etc.
(g) What Bill built — without knowing the term — is a SAX parser. SAX was designed in 1998 for XML processing, but the pattern is older than the name: don't load the whole document into memory. Walk it line by line. Fire events at structural boundaries. Let the consumer decide what to do at each stop. Bill's version does exactly this, three years before SAX was formalized, on fixed-width ASCII mainframe reports instead of XML.
(g) The key is that mainframe reports are fixed-width. Every column is always in the same position. A buyline always starts with three digits, a dash, and a number. A station header always starts with the call letters in column one. A weekly total always starts with spaces and then "TOTAL". No regex — Bill doesn't know regex exists. Just Left$(line, 3) and Mid$(line, 5, 3) — checking character positions directly. It's crude, but it's bulletproof, because the mainframe never deviates from its format. In 1995, Python already exists but Bill has no idea — and won't for another decade. BuyCheck would make a beautiful Python program. Instead it's VB6, string functions, and stubbornness.
The event parser. Each line type has a signature in the first few characters — three digits and a dash means buyline, call letters mean new station, leading spaces and "WEEKLY TOTAL" means a subtotal row. Left$(), Mid$(), IsNumeric() — no regex, no XML, no libraries. The mainframe's rigidity is the API.
(m) At each event, there (mo) was (g) is (m) an opportunity to check, tally(mo) or (g), or (m) do whatever is needed at this point in the parse. A separate module (mo) had (g) has (m) the business rules and (mo) would take (g) takes (m) each event and (mo) issue problems to the object (g) flags problems against the buy. (m) Finally, the object (mo) was presented to a Excel spreadsheet render that was difficult to build (g) is rendered to an Excel spreadsheet — and that part was difficult to build.
(m) Initially, I wanted to use Excel to render the report. It would look just like it came off the mainframe, but portions of the report would be underlined and a comment on the right gutter would be added. This would mimic the manual process and buyers would understand this better than a report of problems. The big issue? (mo) Excel, at the time, just couldn't handle all the automation calls. (g) Excel 97, at the time, just couldn't handle all the automation calls. (m) It would freeze after about 100 processed schedules and break the automation.
(m) So, I (mo) found the specification for the binary format Excel worksheets used. Something called BIFF and it was binary. (g) find the specification for the binary format that Excel worksheets use — something called BIFF (Binary Interchange File Format). (m) The format is intense and I'm not going to be able to just generate the file from the ground up. I pick a different idea. I make templates of 500, 1,000, 5,000 rows or something similar. Each (mo) templates row count was (g) template's row count is (m) based on my observations of "how big" each report could get. The biggest template (mo) was (g) is (m) twice the size of the largest schedule I'd (mo) encounters (g) encountered.
The BIFF template has two columns of bold X's: report text on the left, error gutter on the right. Both columns — all X's, all bold. BuyCheck overwrites every X with real data or spaces. Clean rows get blank gutters (spaces over X's). Flagged rows get error text (words over X's). No bytes added, no bytes removed. When a rule fires, it annotates a TextFragment by name — "flag the daypart" — and the fragment knows its own start/end positions. The rules never know where anything is on the page. Swap the parser for a new buying system — same rules, same interface.
(m) What my code (mo) did, instead of using Excel at all, would be to take (g) does — instead of using Excel at all — is take (m) these templates and simply replace text inside with my text, and adjust underlines that (mo) were (g) are (m) already in the template, to cover the issues. So, from Excel's point of view, I change nothing about the structure, but just change the words/bytes to what I want. No need to learn all the internals. Just find a way to the solution and leave the rest behind.
(g) Think about what this means. BIFF has checksums. It has internal record structures that reference each other by byte offset. You can't add a row. You can't delete a row. You can't change the size of anything. The only safe operation is overwriting existing cell content with content of exactly the same size, and toggling formatting bits that are already allocated. Bill is performing surgery inside a binary file format with a VB6 scalpel — no SDK, no library, no documentation beyond the spec. The buyer opens the result in Excel and sees a perfect spreadsheet. Excel was never running on the machine that made it.
BIFF surgery. Two things to learn from the spec: how to change cell text, and how to move bold/underline over the right columns. Everything else in BIFF? Forget it. The template is a real .xls file — VB6 opens it as raw binary, massages the values, closes it. Excel opens it and sees a perfect spreadsheet. Excel was never involved.
(g) This is the pattern. Don't learn it all — learn what you need when you need it. BIFF is a massive binary specification. Bill learns two things from it: how to change cell text and how to move underlines. The Wrox VB6 book is 800 pages. He reads the chapters that solve the next problem. QBasic shipped free with DOS; he doesn't take a class, he just starts typing. Later, he'll crack HLLAPI — dozens of function codes — and use exactly two: send keys and read screen. If you use something every day, the memorization comes from the repetition. If you don't, it wasn't worth memorizing. He can write SQL blindfolded now, after years of daily use — but triggers? Indexing strategies? How to configure Postgres or SQL Server? Never bothered. Not his problem. He writes queries, not databases. The day the job ends, he hopes to forget SQL forever.
(m) Next, I (mo) needed transport of the reports to me and transport of the audited back to the buyer... (g) need two transport legs: the raw report from the buyer to BuyCheck, and the audited spreadsheet from BuyCheck back to the buyer.
(m) DDS has a program called Piano at the time. This is a (mo) screenscraper off the mainframe to download anything off it. Both structured reports as CSV as well as semi-structured reports as text. (g) screen scraper — it connects to the mainframe and downloads reports. Structured data as CSV, semi-structured reports as text. (m) Each time you request a report, you're asked to give a three(mo) digit (g)-digit (m) code. This helps you as you can filter the print queue on the mainframe to (mo) things just using (g) just (m) that three-digit code. I assign each buyer a new three-digit code. X + your first (mo) init + your second init (g) initial + your last initial. (m) E.g. I (mo) was (g) am (m) XBB.
(m) The Piano program (mo) allowed you to (g) allows you to (m) use wildcards. So, I set it to automatically download any buy schedule where the print code starts with X. Now a buyer can indicate to my copy of Piano that they want to process something (mo) throught the check (g) through the check, (m) and Piano (mo) would set the data locally on a machine (g) drops the data locally. (m) So, inbound transport is settled. Users just issue a "print", but with an X(mo), their initials, and go (g) and their initials, and go. (m) Five minutes later, I have a text file that includes that X code. So, I have it and can tell who to get it back to.
(m) Getting the data back (mo) was the easy bit, and I separate VB6 program handled looking up which buyer is XYZ, and mailing them their responses back. (g) is the easy bit. A separate VB6 program looks up which buyer owns the X code and mails them the response via Outlook automation — another separate VB6 module, so I can swap the transport later without touching anything else.
(m) At this point, I find (mo) a unused (g) an unused (m) machine in the suite, pull it out of the office, buy a hub, a KVM switch, and I install the full solution under my desk. Piano polling for new reports, a VB6 BuyCheck staring at a folder ready to process and place in an outbox, a VB6 MailHandler to look at the outbox, and Outlook being controlled by VB6 to issue new mail. Done and done. Fully online and (mo) always ready (g) always-ready (m) BuyCheck for our buyers to quickly and mechanically evaluate their buys.
(g) Why a whole second machine? This is 1995. There are no virtual machines, no cloud instances, no Docker containers. BuyCheck needs to run all day — polling for new reports, processing them the instant they arrive, mailing results back. If it runs on Bill's desktop, it dies every time he reboots, every time he opens a big spreadsheet, every time IT pushes an update. A dedicated machine means BuyCheck is always on. The KVM switch lets him share one monitor and keyboard between his work PC and the BuyCheck box — flip a switch to check on it, flip back to work. The hub splits his single office Ethernet drop so both machines can reach the network. Total investment: one Staples receipt and a salvaged Pentium nobody wanted. He's just built a server. He doesn't know the word yet.
(g) Four VB6 executables migrate from a Staples hub under a desk, to a server room with a sticky note (admin'd via VNC from upstairs), to an IT-provisioned VM, to the cloud. The infrastructure changes four times. The code changes zero. BuyCheck runs without peer — neither DDS nor Mediaocean ever build anything like it. They finally catch up during their own acquisition, roughly fifteen years late. Even after Bill leaves the agency, BuyCheck keeps running. Nobody touches it. Nobody needs to. One codebase, ~14 years, never rewritten.
(m) It is, as you might imagine, a huge hit.
(g) Here's what it looks like:
(g) The buyer never touches BuyCheck. They never see it. They never install it. They type their X code into a DDS print request — the same workflow they already use every day — and a minute later, an annotated Excel spreadsheet lands in their inbox showing exactly which lines fail which spec rules, underlined in place, with plain-English notes in the margin. Self-service without anyone knowing it's self-service.
(g) A job queue, a rules engine, a binary file renderer, and a notification system — built from a dead PC, a Staples hub, a KVM switch, and four VB6 executables, running under a desk on Wilshire Boulevard in 1995.
(g) BuyCheck multiplied. New clients, new specs, new rule modules — same parser, same ghost machine, same pattern. Bill became the guy who builds things. Two-week turnaround on any problem. The "Bill Berger Special." Not afraid of any project. Not because he was brave. Because he'd already typed PANIC into a dying NeXT Cube while a man in Irvine screamed at him to throw it out a window. Everything after that is just software.
(g) Meanwhile, the industry caught up — sort of. DDS eventually retired the 3270 green screen and shipped a Windows application called Spectra DS, built on an AS/400 backend. Three decades of buyer requests baked into one window. Eight tabs. Three rows of buttons. Every edge case anyone ever asked for, crammed into a single cockpit. It is, in its way, the opposite of how Bill builds things.
What the industry "upgraded" to: Spectra DS on an AS/400 backend. Eight tabs. Three rows of buttons. Every edge case a buyer demanded for thirty years, crammed into one window. This is still in use in 2026.
(g) Bill's approach was the opposite: find the seam, learn two things, ignore the rest. The industry's approach was to learn everything and put it all on one screen. Spectra is still running in 2026. So is the pattern Bill figured out in a conference room in 1995.
(g) He didn't know that the event-driven parser was called SAX. He didn't know that the ghost machine was a message queue. He didn't know that the pluggable rules were separation of concerns, or that the BIFF templates were binary serialization, or that the X codes were job routing. He didn't know any of the names. He just needed to solve the next problem.
(g) That's how a philosophy major ends up a Senior Vice President of Data Engineering, managing six Snowflake accounts across five countries. Not because someone drew him a career path. Because a fast food client shredded two reams of paper, and he never wanted to be in that conference room again.
← All case studies · Next: Making DDS Beg for Mercy → · Coming: Bill Berger Specials
Disclosure: This page was written collaboratively by Bill Berger and Claude (Anthropic). Bill wrote the story from memory. Claude provided historical context, hyperlinks, technical explanations, copy edits, and the closing paragraphs. Use the toggle above to see exactly who wrote what.