良いコードの6つの戒め:時の試練に耐えるコードを書く

公開: 2022-03-11

人間は、およそ半世紀の間、コンピュータプログラミングの芸術と科学に取り組んできただけです。 ほとんどの芸術や科学と比較して、コンピュータサイエンスは多くの点でまだ幼児であり、壁に足を踏み入れ、自分の足でつまずき、時にはテーブルを横切って食べ物を投げます。

その相対的な若さの結果として、「良いコード」の適切な定義が進化し続けているので、その定義が何であるかについて、私たちはまだコンセンサスを持っていないと思います。 「良いコード」とは、100%のテストカバレッジを持つコードであると言う人もいます。 他の人は、それが超高速でキラーなパフォーマンスを持ち、10年前のハードウェアで問題なく動作すると言うでしょう。

これらはすべてソフトウェア開発者にとって称賛に値する目標ですが、私は別の目標を組み合わせて、保守性に挑戦します。 具体的には、「優れたコード」とは、組織が(作成者だけでなく)簡単かつ容易に保守でき、作成されたスプリントよりも長持ちするコードです。以下は、私が発見したいくつかのことです。米国および海外の大企業および小規模企業でのエンジニアとしてのキャリアは、保守可能な「優れた」ソフトウェアと相関しているようです。

ただ「機能する」コードに甘んじることはありません。 優れたコードを書く。
つぶやき

戒め#1:あなたのコードをあなたが望む方法で他人のコードがあなたを扱うように扱う

私はあなたのコードの主な対象者がコンパイラ/コンピュータではないことを書いた最初の人からはほど遠いですが、次にコードを読み、理解し、維持し、強化する必要がある人は誰でも(これは必ずしも今から6か月後にあなたになるとは限りません) )。 お金を払う価値のあるエンジニアなら誰でも、「機能する」コードを作成できます。 優れたエンジニアの特徴は、ビジネスを長期的にサポートする保守可能なコードを効率的に記述でき、問題を簡単かつ明確かつ保守可能な方法で解決するスキルを備えていることです。

どのプログラミング言語でも、良いコードでも悪いコードでも書くことができます。 プログラミング言語が優れたコードの記述をどれだけ容易にするかによって判断すると仮定すると(とにかく、少なくとも最上位の基準の1つである必要があります)、プログラミング言語は、その使用方法(または悪用)に応じて「良い」または「悪い」可能性があります。 )。

多くの人が「クリーン」で読みやすいと見なしている言語の例はPythonです。 言語自体は、ある程度の空白の規律を強制し、組み込みのAPIは豊富で、かなり一貫性があります。 とはいえ、言いようのないモンスターを作成することは可能です。 たとえば、クラスを定義し、実行時にそのクラスのすべてのメソッドを定義/再定義/定義解除することができます(モンキーパッチと呼ばれることがよくあります)。 この手法は、当然、せいぜい一貫性のないAPIにつながり、最悪の場合、モンスターをデバッグすることは不可能になります。 素朴に考える人もいるかもしれませんが、「確かに、誰もそうしません!」 残念ながら、彼らはそうします、そしてあなたが彼らのAPIのコアとしてモンキーパッチを広範囲に(乱用する)使用する実質的な(そして人気のある!)ライブラリに出くわす前にpypiを閲覧するのに長い時間はかかりません。 最近、オブジェクトのネットワーク状態に応じてAPI全体が変化するネットワークライブラリを使用しました。 たとえば、 client.connect()を呼び出して、 HostNotFoundNetworkUnavailableの代わりにMethodDoesNotExistエラーが発生する場合があるとします。

戒め#2:良いコードは、部分的にも全体的にも簡単に読まれ、理解されます

優れたコードは、部分的および全体的に、他の人(および将来的には「私は本当にそれを書いたのか?」症候群を回避しようとする作者)によって簡単に読み取られ、理解されます。

「部分的に」とは、コード内のモジュールまたは関数を開くと、コードベースの残りの部分全体を読むことなく、それが何をするのかを理解できるはずであることを意味します。 可能な限り直感的で自己文書化する必要があります。

コードベースの他の(一見無関係な)部分からの動作に影響を与える詳細を常に参照するコードは、すべての文の終わりにある脚注または付録を参照する必要がある本を読むようなものです。 あなたは最初のページを通過することは決してないでしょう!

「ローカル」の読みやすさに関するその他の考え:

  • 適切にカプセル化されたコードは読みやすくなる傾向があり、あらゆるレベルで関心の分離が行われます。

  • 名前は重要です。 Thinking Fast and Slowのシステム2をアクティブにして、脳が思考を形成し、実際の注意深い思考を変数名とメソッド名に入れます。 数秒余分に支払うと、かなりの利益が得られます。 名前の付いた変数はコードをより直感的にすることができますが、名前の付けられていない変数はヘッドフェイクや混乱を招く可能性があります。

  • 賢さは敵です。 凝ったテクニック、パラダイム、または操作(リスト内包表記や三項演算子など)を使用するときは、コードを短くするだけでなく、読みやすくする方法で使用するように注意してください。

  • 一貫性は良いことです。 中括弧の配置方法と操作の両方の点でスタイルの一貫性により、読みやすさが大幅に向上します。

  • 関心事の分離。 特定のプロジェクトは、コードベースのさまざまなポイントで、ローカルで重要な無数の仮定を管理します。 コードベースの各部分を、これらの懸念事項のできるだけ少ないものに公開します。 人物オブジェクトの姓がnullになることがある人物管理システムがあるとします。 人のオブジェクトを表示するページにコードを書いている人にとって、それは本当に厄介かもしれません! そして、「コードベースが持っている厄介で明白でない仮定」のハンドブックを維持しない限り(私は知らない)、表示ページのプログラマーは、名前がnullになる可能性があることを知らず、おそらくnullポインターを使用してコードを記述します。姓がnullの場合が表示された場合の例外。 代わりに、コードベースのさまざまな部分が相互に対話するために使用する、よく考えられたAPIとコントラクトを使用してこれらのケースを処理します。

戒め#3:優れたコードには、状態の管理を明確にするためのよく考えられたレイアウトとアーキテクチャがあります

国家は敵です。 なんで? これは、アプリケーションの中で最も複雑な部分であり、非常に慎重かつ慎重に処理する必要があるためです。 一般的な問題には、データベースの不整合、新しいデータがどこにも反映されない部分的なUIの更新、順序が狂った操作、またはifステートメントとブランチがどこにでもある複雑なコードに気を配り、コードの読み取りが困難になり、コードの保守がさらに困難になることが含まれます。 状態をペデスタルに配置して細心の注意を払い、状態へのアクセス方法と変更方法に関して非常に一貫性があり、慎重に行うことで、コードベースが大幅に簡素化されます。 一部の言語(Haskellなど)は、プログラムおよび構文レベルでこれを強制します。 外部状態にアクセスしない純粋関数のライブラリがあり、外部の純粋関数を参照するステートフルコードの小さな表面積がある場合、コードベースの明確さがどれほど向上するかに驚かれることでしょう。

戒め#4:良いコードは車輪の再発明ではなく、巨人の肩の上に立つ

車輪の再発明を行う可能性がある前に、解決しようとしている問題がどれほど一般的であるか、または実行しようとしている機能がどれほど一般的であるかを考えてください。 誰かがあなたが活用できるソリューションをすでに実装しているかもしれません。 適切で利用可能な場合は、時間をかけてそのようなオプションについて考え、調査してください。

とは言うものの、完全に合理的な反論は、依存関係はマイナス面なしに「無料」で提供されるわけではないということです。 いくつかの興味深い機能を追加するサードパーティまたはオープンソースライブラリを使用することにより、そのライブラリにコミットし、依存するようになります。 それは大きなコミットメントです。 それが巨大なライブラリであり、ほんの少しの機能が必要な場合、たとえばPython 3.xにアップグレードする場合、ライブラリ全体を更新する負担が本当に必要ですか? さらに、バグが発生した場合、または機能を拡張したい場合は、修正または拡張を提供するのは作成者(またはベンダー)に依存するか、オープンソースの場合は(潜在的に実質的な)コードベースは、機能のあいまいな部分を修正または変更しようとすることに完全に慣れていません。

確かに、依存しているコードがよく使用されているほど、メンテナンスに時間を費やす必要が少なくなります。 結論としては、独自の調査を行い、外部テクノロジを含めるかどうか、およびその特定のテクノロジがスタックに追加するメンテナンスの量を独自に評価することは価値があります。

以下は、プロジェクトで現代に再発明すべきではないもののより一般的な例のいくつかです(これらがプロジェクトでない限り)。

データベース

プロジェクトに必要なCAPを特定し、適切なプロパティを持つデータベースを選択します。 データベースはもはやMySQLを意味するだけでなく、次の中から選択できます。

  • 「従来の」スキーマ化されたSQL: Postgres / MySQL / MariaDB / MemSQL /AmazonRDSなど。
  • キーバリューストア: Redis / Memcache / Riak
  • NoSQL: MongoDB / Cassandra
  • ホストされているDB: AWS RDS / DynamoDB / AppEngine Datastore
  • 重量挙げ: Amazon MR / Hadoop(Hive / Pig)/ Cloudera / Google Big Query
  • クレイジーなもの: ErlangのMnesia、iOSのコアデータ

データ抽象化レイヤー

ほとんどの場合、使用することを選択したデータベースに生のクエリを記述しないでください。 ほとんどの場合、DBとアプリケーションコードの間にライブラリが存在し、データベースセッションの同時管理とスキーマの詳細の問題をメインコードから分離します。 少なくとも、アプリケーションコードの途中に生のクエリやSQLをインラインで配置しないでください。 むしろ、それを関数でラップし、すべての関数を本当に明白なもの(たとえば、「queries.py」)と呼ばれるファイルに一元化します。 たとえば、 users = load_users()のような行は、 users = db.query(“SELECT username, foo, bar from users LIMIT 10 ORDER BY ID”)よりもはるかに読みやすくなっています。 このタイプの集中化により、クエリで一貫したスタイルを使用することがはるかに簡単になり、スキーマが変更された場合にクエリを変更する場所の数が制限されます。

活用を検討するその他の一般的なライブラリとツール

  • キューイングまたはPub/Subサービス。 AMQPプロバイダー、ZeroMQ、RabbitMQ、AmazonSQSを選択してください
  • 保管所。 Amazon S3、Google Cloud Storage
  • モニタリング: Graphite / Hosted Graphite、AWS Cloud Watch、New Relic
  • ログ収集/集約。 Loggly、Splunk

自動スケーリング

  • 自動スケーリング。 Heroku、AWS Beanstalk、AppEngine、AWS Opsworks、Digital Ocean

戒め#5:小川を渡らないでください!

プログラミングデザイン、pub / sub、アクター、MVCなどに適したモデルはたくさんあります。好きなものを選んで、それに固執してください。 さまざまな種類のデータを処理するさまざまな種類のロジックをコードベースで物理的に分離する必要があります(この場合も、関心の分離の概念と将来の読者の認知的負荷の軽減)。 たとえば、UIを更新するコードは、UIに入るものを計算するコードとは物理的に異なる必要があります。

戒め#6:可能であれば、コンピューターに仕事をさせましょう

コンパイラがコード内の論理エラーをキャッチし、不正な動作、バグ、または完全なクラッシュを防ぐことができる場合は、それを絶対に利用する必要があります。 もちろん、一部の言語には、これを他の言語よりも簡単にするコンパイラがあります。 たとえば、Haskellには有名な厳密なコンパイラがあり、プログラマーはコードをコンパイルするためだけにほとんどの労力を費やしています。 ただし、コンパイルすると、「正常に機能します」。 強く型付けされた関数型言語で書いたことがない人にとっては、これはばかげているか不可能に思えるかもしれませんが、私の言葉を信じないでください。 真剣に、これらのリンクのいくつかをクリックしてください、実行時エラーなしで世界に住むことは絶対に可能です。 そしてそれは本当にその魔法です。

確かに、すべての言語に、コンパイル時のチェックに役立つコンパイラまたは構文があるわけではありません。 そうでない場合は、プロジェクトで有効にできるオプションの厳密性チェックを調べて、それらが適切かどうかを評価してください。 ランタイムが寛大な言語で最近使用した一般的なものの短い、包括的でないリストは次のとおりです。

  • Python:pylint、pyflakes、警告、emacsの警告
  • Ruby:警告
  • JavaScript:jslint

結論

これは、「良い」(つまり、簡単に保守できる)コードを作成するための戒めの網羅的または完全なリストではありません。 とは言うものの、将来私が取得しなければならなかったすべてのコードベースがこのリストの概念の半分にさえ従えば、私は白髪がはるかに少なくなり、私の人生の終わりにさらに5年を追加できるかもしれません。 そして、私は確かに仕事がより楽しく、ストレスが少ないと思うでしょう。