コードで避けるべきアンチパターン

すべての開発者は、構造化された、単純に計画された、適切にコメントされたコードを記述したいと考えています。従うべき明確なルールと心に留めておくべきフレームワークを私たちに与える無数のデザインパターンさえあります。

しかし、しばらく経って書かれたソフトウェアや、あまりにも早く書かれたアンチパターンを見つけることができます。

問題を迅速に解決するための無害な基本的なハックは、コードベースに前例を作ることができます。複数の場所にコピーして、対処する必要のあるアンチパターンに変えることができます。

では、アンチパターンとは何ですか?

ソフトウェアでは、アンチパターンは、コードで繰り返し発生する問題を解決しない方法を説明する用語です。アンチパターンは悪いソフトウェア設計と見なされ、通常は効果がないか、あいまいな修正です。  

また、通常、「技術的負債」も追加されます。これは、後で戻って適切に修正する必要があるコードです。

この記事で説明する6つのアンチパターンは、スパゲッティコードゴールデンハンマーボートアンカーデッドコードコードの増殖、および神オブジェクトです。

スパゲッティコード

スパゲッティコードは、最もよく知られているアンチパターンです。構造がほとんどないコードです。

モジュール化されたものはありません。ランダムなディレクトリにランダムなファイルが散らばっています。流れ全体を追跡するのは難しく、(スパゲッティのように)完全に絡み合っています。

通常、これは誰かが事前にプログラムの流れを注意深く考えておらず、コーディングを始めたばかりの問題です。

それは何をするためのものか?!私はこれに従うことができません

image.png

これはメンテナンスの悪夢であるだけでなく、新しい機能を追加することもほぼ不可能になります。

そのような考古学/推測作業を行うときに発生する無数の問題を予測することは不可能であるため、常に物事を壊したり、変更の範囲を理解しなかったり、作業の正確な見積もりを出したりします。

スパゲッティコードのアンチパターンについて詳しくは、こちらをご覧ください。

ゴールデンハンマー

「もしあなたが持っている道具がハンマーだけなら、すべてを釘のように扱うのは魅力的だと思います。」アブラハム・マズロー

私と一緒にシナリオを想像してみてください。あなたの開発チームは、まったく新しいハンマーアーキテクチャで非常に有能です。それはあなたの過去のすべての問題に対して素晴らしく機能しました。あなたは世界をリードするハンマーアーキテクチャチームです。

しかし今、どういうわけか、すべてが常にこのアーキテクチャを使用することになります。平頭ねじ?ハンマー。プラスネジ?ハンマー。六角レンチが必要ですか?いいえ、ハンマーで叩きます。

あなたはあなたが必要とするものに完全には適合しないが仕事を成し遂げる建築的アプローチを適用し始めます。あなたは1つのパターンに過度に依存しており、最高の仕事のための最高のツールを学ぶ必要があります。

正方形を円形に押し込もうとしているため、プログラム全体がパフォーマンスに深刻な打撃を与える可能性があります。コードを記述し、この問題に対してハンマーアーキテクチャを使用してプログラムを実行するには、2倍の時間がかかることをご存知ですが、それは簡単であり、快適です。

また、それはあまり予測できません。さまざまな言語には、直面する問題に対する共通の修正と、独自の標準があります。ある言語でうまく機能したすべてのルールを次の言語に問題なく適用することはできません。

あなたのキャリアの中で一貫して学ぶことを怠らないでください。問題に適した言語を選択してください。アーキテクチャについて考え、快適ゾーンを押し出してください。直面している問題に取り組むための新しいツールと新しい方法を調査して調査します。

ゴールデンハンマーのアンチパターンについて詳しくは、こちらをご覧ください。

ボートアンカー

ボートアンカーのでプログラマはコードベースでコードを残してどこアンチパターンがあり、彼らはそれを後で必要になる場合があります。

彼らは仕様から少し外れたものをコーディングし、それはまだ必要ではありませんが、来月になると確信しています。だから彼らはそれを削除したくないのです。それを本番環境に送信し、後で必要になったときに、すぐに機能させることができます。

しかし、これは、廃止されたコードをすべて含むコードベースでメンテナンスの悪夢を引き起こします。大きな問題は、同僚がどのコードが廃止され、フローを変更しないのかを、変更するコードと比較して理解するのに苦労することです。

Imagine you are on a hot fix, and are desperately trying to work out what is responsible for sending customers' card details to the API to withdraw funds from their bank. You could waste time reading and debugging obsolete code, without realising you aren't even in the right place in the codebase.

The final issue is, obsolete code makes your build time longer and you may mix-up working and obsolete code. You could even start to inadvertently "turn it on" in production.

Now you can probably see why it's called the boat anchor anti-pattern – it is heavy to carry (adds technical debt) but doesn't do anything (quite literally, the code serves no purpose, it doesn't work).

You can read more here about the Boat anchor anti-pattern.

Dead code

Have you ever had to look at code written by someone who doesn't work at your company any longer? There's a function that doesn't look like it is doing anything. But it is called from everywhere! You ask around and no-one else is quite sure what it's doing, but everyone's too worried to delete it.

Sometimes you can see what it's doing, but the context is missing. You are able to read and understand the flow, but why? It doesn't look like we need to hit that endpoint anymore. The response is always the same response for every different user.

This is commonly described as the Dead code anti-pattern. When you can't see what is "actual" code necessary to the flow and successful execution of your program, versus what was only needed 3 years ago, and not now.

This particular anti-pattern is more common in proof on concept or research code that ended up in production.

One time at a tech meet up I met a guy who had this exact problem. He had tons of dead code, which he knew was dead, and lots he suspected was dead. But he could not get permission from management to ever remove all the dead code.

He referred to his approach as Monkey testing, where he started to comment out and turn off things to see what blew up in production. Maybe a little too risky!

If you don't fancy Monkey testing your production app, try to frame technical debt to management as "technical risk" to better explain why you think it's so important to tidy up.

Or even write down everything your particular module/section does you want to re-write, and take an iterative approach to remove piece by piece the dead code. Checking every time you haven't broken anything.

You don't have to drop a huge rewrite with thousands of changes. But you will either understand why it's so crucial and document why it's needed, or delete the dead code as you desired.

You can read more here about the Dead code anti-pattern.

Proliferation of Code

Objects or modules regularly communicate with others. If you have a clean, modularised codebase you often will need to call into other separate modules and call new functions.

The Proliferation of Code anti-pattern is when you have objects in your codebase that only exist to invoke another more important object. Its purpose is only as a middleman.

This adds an unnecessary level of abstraction (adds something that you have to remember) and serves no purpose, other than to confuse people who need to understand the flow and execution of your codebase.

A simple fix here is to just remove it. Move the responsibility of invoking the object you really want to the calling object.

You can read more here about the Proliferation of Code anti-pattern.

God Object

If everywhere in your codebase needs access to one object, it might be a God object.

God objects do too much. They are responsible for the user id, the transaction id, the customer's first and last name, the total sum of the transaction, the item/s the user is purchasing...you get the picture.

It is sometimes called the Swiss Army Knife anti-pattern because you only really need it to cut some twine, but it also can be a nail file, saw, pair of tweezers, scissors, bottle opener and a cork screw too.

In this instance you need to separate out and modularise your code better.

Programmers often compare this problem to asking for a banana, but receiving a gorilla holding a banana. You got what you asked for, but more than what you need.

The SOLID principles explicitly discuss this in object orientated languages, to help us model our software better (if you don't know what the SOLID principles are, you can read this article).

The S in the acronym stands for Single Responsibility - every class/module/function should have responsibility over one part of the system, not multiple.

You can see this problem over and over again, how about the below interface?

interface Animal { numOfLegs: string; weight: number; engine: string; model: string; sound: string; claws: boolean; wingspan: string; customerId: string; } 

Can you see by even just briefly scanning this interface that the responsibility of this is far too broad, and needs refactoring? Whatever implements this has the potential to be a God object.

How about this?

 interface Animal { numOfLegs: string; weight: number; sound: string; claws: boolean; } interface Car { engine: string; model: string; } interface Bird { wingspan: string; } interface Transaction { customerId: string; } 

Interface segregation will keep your code clear about where the responsibilities lie, and stop forcing classes that only need wingspan to also implement the engine, customerId and model  and so on.

神オブジェクトのアンチパターンについて詳しくは、こちらをご覧ください。

結論

大規模なコードベースでは、技術的負債の管理、新しい開発の開始、および製品のバグのキューの管理の間で一定のバランスがあります。

この記事が、アンチパターンのうさぎの穴を降りるときに気付く目と、それをきれいに解決するためのいくつかのツールを提供してくれることを願っています。

この記事を楽しんでいただき、もっと見たいと思ったら、Twitterで私の文章を共有します。