iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
😓

When Technical Selection Leads to a Dead End

に公開

Introduction

Technology selection is an exciting and engaging topic. It is fun to think about using popular languages, implementing quickly with familiar languages, or trying out trendy frameworks. However, I am going to talk about how systems launched with such high hopes can become "frozen" or unmaintainable due to mistakes in technology selection.

Please note that real-world examples have been heavily obscured, so you may have many questions, but I hope for your understanding.

Stories of Getting Stuck

Getting Stuck During Handover

This is a story about how failing to align technology stacks leads to dead ends during project handover.

The IE7 Constraint

I was tasked with building a sub-system with about a dozen screens using .NET MVC and a specific API. I was brought in as emergency support for this sudden requirement. It had been a while since I used C#, and since convenient syntax is added with every version, I was excited to participate.

However, there was a constraint: it had to run on IE7, which was barely supported at the time. Furthermore, it turned out that no one on the team knew who would be the engineer taking over the project after I finished.

The Birth of a "Homebrew" Framework

(Homebrew framework: A framework for local, specific purposes)

I ran into trouble with the screen used to manage the display order of items. The requirement was to reorder items and save the changes with a button, but reordering in IE7 is not straightforward. Changing this via JavaScript is quite a hassle.

To get a bit technical, if this were a modern browser, we could use Flexbox to change the order via CSS properties. With reactive architectures like React or Vue.js, the HTML would update just by changing the data order.

That was when a bad idea popped into my head: "Isn't a simple MVVM easy enough?" I spent a few hours building a prototype, and it worked well, so I spent another two days completing a 300-line "homebrew" framework, complete with comments.

Thanks to this, I was able to implement requirement changes easily and with fewer bugs, resulting in a comfortable development process.

"It's fine!"

Development finished, delivery went smoothly, and I finally met the engineer taking over. I explained the program I had developed and the simple MVVM framework I had created.

Sweating, I told them something confusing like this:

"The operation is a simple structure where we define a generator that creates HTML from an array of items, and pass search and operation functions as arguments. Searched items are regenerated by the generator each time. The re-rendering is instantaneous, so the user won't even notice."

A problem with introducing a framework is that before implementing, one must understand the unique philosophy of that framework. Someone who has never touched a framework using this type of MVVM cannot simply understand it overnight.

I was expecting a response like this:

"Don't build things that no one can understand!"

However, the reply was:

"It's fine!"
"Oh, um, sorry! Let me explain in more detail..."
"No, it's fine!"

I knew for sure that it was not fine. Regardless of technical skill, there are always areas one hasn't handled before. They probably realized it was impossible to understand immediately and that my explanation wasn't clear enough to grasp it.

And Then, Frozen

In all likelihood, that part became unmaintainable forever. In this case, I feel it would have been better to write spaghetti code using orthodox jQuery, even if it was complex and buggy. From my perspective, I had been entrusted with a wide scope of work and did everything with maximum performance.

In environments where the appropriate platform cannot be chosen, strange code is often produced, and that is something that could happen to anyone.

Getting Stuck by Choosing Minor/Experimental Features

Just because a function is available in a framework doesn't mean you should use it.

The Inherited System Used Experimental Features

When I became in charge of a certain Web system, it was using Vuetify. Think of Vuetify as a framework for Vue.js that makes things look nice.

No Manual!

This Web system was using Vuetify 2. When I tried to change some table-related features, it turned out that it didn't work as described in the official documentation.

Looking at other source code, I saw settings not found in the official documentation. Looking closely, I realized they were items labeled as experimental.

It seems that in Vuetify, items marked as experimental are features that do not guarantee backward compatibility, even in minor version updates. The version used was old, and the official site only had documentation for the latest version. I thought, "Do I really have to check the source code?"

Brute Force: Rebuilding the Official Site

Looking closely at the official documentation, I realized the official site was also registered on GitHub.

So, I downloaded it, called up the source code for the relevant version, and set up the old version's official documentation server locally to verify. As a result, I was able to obtain the details of the configuration items.

Use Stable Versions

Since tables were used throughout the entire system, there were many changes, and version upgrades take considerable effort and cannot be fixed on the side.

I learned that settings that only apply to minor versions should not be used widely. If possible, use stable versions.

In the current stable version, 2.6, table-related settings are no longer experimental. It is very intuitive and organized, making it a much better framework.

Getting Stuck on Migration Costs

This is a story about how upgrading major versions is much tougher than minor versions as mentioned before.

A PHP 7 Project

It was a Web system using a PHP 7 framework.

The Framework Had No Backward Compatibility

As those with professional experience in PHP know, PHP 8 made major changes to parts that had received a lot of criticism and became a modern language. At the same time, the framework used at that time was also completely rewritten from scratch.

Is a Full Rewrite the Only Option?

Considering the time it was built, the project was using the latest version of PHP, and there wasn't much that could have been foreseen regarding the framework. As a result, it was judged that a full-scratch rebuild was necessary, but making that investment was difficult.

Getting Stuck on Person-Dependence (Siloing)

This is an example where I wasn't satisfied with experimental features and used unofficial methods. In this case, it was localized, so it didn't cause major problems.

Heavy JOINs

There was a ticket asking to improve the performance of a search screen in a Rails Web system because it was very slow. It was performing partial-match searches on complex joins. Partial-match searches generally do not use indexes and become heavy processes.
Due to the complex joins, the SQL issued by Rails became complicated. As a result, I ended up writing SQL directly.

Use of Private Methods

I will omit the details, but writing SQL directly in Rails is not a standard approach. Upon investigation, I had to use private methods (more accurately called private APIs).
There was no documentation, and technical blogs could not be used as-is because the versions were different. Therefore, I read the code directly to confirm the specifications.
To maximize search speed, I edited the SQL dynamically, and it was quite a struggle to research how to convert the results into ActiveRecord objects, but I was able to achieve the target search response time.

Soft Landing

I knew from the beginning that the code would be vastly different in difficulty compared to other parts. Also, since they are private methods, I was concerned that they might stop working in minor version upgrades. Therefore, I limited such handling to this specific area and included very thorough comments, such as links to blog articles. Naturally, I also added sufficient tests.
When a problem occurs during a version upgrade, you must be prepared to invest the man-hours to replace it.

Getting Stuck on Hiring

Surprisingly, few people consider this point. It does not mean that technology selection is unnecessary even in the maintenance phase.

Struggles of Changing Jobs as a COBOLer

The first language I learned professionally was COBOL. In the job market, being a COBOL engineer was treated as only being able to write COBOL, and getting work in other languages was treated the same as being an inexperienced recruit.
At that time, there were no sites to measure coding ability, and the hurdles and costs for building a portfolio site were quite different from today.
As a result, I struggled to find my next workplace.

Young People Also Avoid Old Technology

Similar things still exist today. Programmer-focused job sites still ask for years of experience per language, and companies filter based on that. Furthermore, there is a possibility of being rejected in interviews if you only have experience with old versions. This is a problem of the practice of prioritizing experience over ability.
Conversely, young people do not want to grow old in vain without gaining experience. As a result, moving early becomes the optimal career choice.
In fact, when I learned that an acquaintance's internship workplace was not using services that should be considered standard, I advised them to move on after about a few months.

Old Technology Is Actually a Vital Issue

When trying to supplement personnel for a system where a labor shortage has become clear in that way, you will struggle with hiring. Recently, I have heard that in the case of COBOL, the unit price has conversely gone up.
Ensuring highly capable engineers in such a situation is a difficult task, and it is expected that systems left to low-capability engineers for several years will become impossible to upgrade.
I understand that management wants to keep maintenance costs as low as possible, but I think it should also be understood that a system becoming impossible to change is also a crisis for management.

What Should Be Done?

Divide and conquer. Minimize the scope of influence.

It Is Harder to Get Stuck If You Have Tests

It is not a magic wand or a silver bullet, but tests work effectively in any case.

Man-hours Become Visible

The amount of testing is the amount of specification. You can roughly grasp the man-hours required for migration from the amount of affected tests.

Migration Is Possible

When you migrate, what needs to be verified is clear, so selecting the target for migration is easy. Naturally, the things to check when you migrate will decrease.

Use Wrappers to Limit Change Targets

In the case of replacing large frameworks like Rails or Laravel, there is nothing you can do, so please take sufficient time for selection. Here, I deal with things that are somewhat localized.

What Is a Wrapper?

A wrapper is a way of calling a specific object from another object. The advantage is that you can clean up the interface by doing this.
For example, if there is an official API for using SaaS, you should only perform the calls within a wrapper class and make the wrapper class's interface as simple as possible.

In the End, There Are Code Changes

In reality, even if you have a wrapper class, there are often cases where you need to change how it is called. However, it will not be in vain.
Because you will no longer have to worry about impact analysis, there will be no inconsistencies in calling, and changes should be much easier.

On AI Coding

I use Claude Code as an AI coding agent via LLMs. It has changed the common sense of coding so far.

With tests and proper code, switching selected technologies might be performed smoothly.

Summary

  • Anyone can fail in selection
  • Expect a change of direction
  • Write tests
  • Cleaner code is better, isn't it?

Discussion