I have a very similar experience to parent. Except I inherited the program after the original programmer was fired and the company hired two programmers to try to upgrade their backend to PHP. And then they hired me because some "consultant" (really just a cousin of a friend or smth) told the company owner that they need RoR, and so I was tasked with porting everything to RoR while the other two guys were still porting it to PHP...

Eventually the three of us managed to convince the owner that maybe RoR porting wasn't such a great idea and joined forces to make the PHP thing work.

It was the time that PHP had, basically, only Drupal and Joomla to organize a typical backend for this kind of business. Zend framework was in the early stages, but definitely didn't catch on yet. Kohana, Laravel haven't been around yet.

Now, I'm not really even a Web developer, and my familiarity with PHP is... not very deep. Before then, I'd only worked on Web projects that were in either Java, Python or RoR, and usually the Web part wasn't where I was assigned to. I'd usually work on internal company's infrastructure for the project and less on the project itself.

Anyways, the first thing that was drastically different in the existing CGI from anything I'd work on before then was the "routing". I.e. all other Web frameworks tried to keep the definition of what are the things accessible on your Web site in one place. In some hierarchical fashion. This, I believe, why the ideas s.a. REST and Swagger caught on: conceptually they resembled the way developers thought about their Web applications. If there was a $site/user URL, then it was natural and expected that $site/user/shopping-cart would be where you'd find the information about what that user wanted to buy. CGI scripts, at least those I've inherited had nothing of the sort. Since this was ASP Classic, it was just a directory with files named like shopping_cart.vb or checkout.js.

Why this was awful: in a hierarchical routing definition a developer had no problem answering questions s.a. "is this endpoint supposed to be reachable?". Also, since these routing schemes often mapped to classes / some kind of other internal program hierarchy, it was quite obvious to the developer in what state (what session variables or cookies will be present) a certain endpoint might be visited. With CGI it was neigh impossible to tell if it's legal for the particular page to be displayed when a particular session variable isn't set or not.

Especially, given the age of the project and the lack of discipline and general understanding of version control on the part of the previous authors the project often contained multiple but slightly different copies of what conceptually would've been the same page. There'd be stuff like "shopping_cart Copy (1).vb" which you'd think was definitely a garbage leftover from someone copying files accidentally by dragging them with the mouse, and you'd be wrong... There'd also be "user_login_from_company_X_using_phone.js" which you'd later discover that company X went out of business at least five years ago.

A lot of these things are automatically prevented by using frameworks, where the authors would have to expend extra effort to cause the mayhem I've witnessed in this CGI application. While frameworks "stifle the creativity" of the Web programmers by making them all follow the same beaten path, knowing the average quality of Web programmer output, when not confined to the rules of such a framework, things go south very fast.

So, in practice, I'll say, it's a good thing people don't use CGI as much anymore. Average programmer is a bad programmer. If you have commercial goals to hit, the loss of freedom and creativity are more than compensated by the better guarantees on the lower margin on the product quality.