web-dev-qa-db-ja.com

廃止予定のコードが期限に達した後にコンパイルされないようにする

私のチームでは、大きなモノリシックプロジェクト(クラス全体、メソッドなど)の多くの古いものをクリーニングしています。

そのクリーニング作業中に、通常の@Deprecatedよりも洗練された種類の注釈またはライブラリがあるかどうか疑問に思いました。この@FancyDeprecatedは、特定の日付が経過した後に古い未使用のコードをクリーンアップしていない場合、プロジェクトのビルドが成功しないようにする必要があります。

私はインターネットで検索しましたが、以下に説明する機能を持つものは見つかりませんでした:

  • 特定の日付の前に削除する予定のコードに配置するための注釈または類似のものである必要があります
  • その日付の前にコードがコンパイルされ、すべてが正常に動作します
  • その日付以降、コードはコンパイルされず、問題について警告するメッセージが表示されます

私はユニコーンを探していると思います...プログラム言語に同様の技術はありますか?

計画Bとして、「デッドライン」で失敗し始める、削除対象のコードの単体テストで魔法をかける可能性を考えています。これについてあなたはどう思いますか?もっと良いアイデアは?

68
Arcones

コンパイルが本当に禁止されている場合、これは便利な機能ではないと思います。 2018年6月1日の時点で、前日にコンパイルされたコードの大部分がコンパイルされない場合、チームはその注釈をすばやく削除し、コードをクリーンアップしたかどうかを確認します。

ただし、次のようなカスタム注釈をコードに追加できます。

@Deprecated_after_2018_07_31

そして、これらの注釈をスキャンする小さなツールを構築します。 (リフレクションを利用したくない場合は、grepの単純な1つのライナーでそれを実行できます)。 Java以外の言語では、「把握」に適した標準化されたコメント、またはプリプロセッサ定義を使用できます。

次に、特定の日付の直前または直後にそのツールを実行します。それでもその注釈が見つかった場合は、コード部分を早急にクリーンアップするようチームに通知してください。

62
Doc Brown

これは時限爆弾として知られる機能を構成します。 時間爆弾を作成しないでください。

コードは、どれだけうまく構造化して文書化しても、特定の年齢を超えて存続すると、は理解されていない神話に近いブラックボックスになります。将来の誰もが最後に必要とするものはまだ別の奇妙な障害モードであり、予想外の最悪の時期に、明らかな救済策なしにそれらを完全にキャッチします。このような問題を故意に生じさせる言い訳は絶対にありません。

このように見てください:コードベースが整理されていて、陳腐化を気にし、それに従っている場合は、メカニズムは必要ありませんwithin通知するコード。そうでない場合は、コードベースの他の側面についても最新ではない可能性があり、おそらくアラームにタイムリーかつ正確に応答することができません。言い換えれば、時限爆弾は誰にとっても良い目的を果たしません。いやだっていうだけだよ!

283
Kilian Foth

C#では、次のようにObsoleteAttributeを使用します。

  • バージョン1では、機能を出荷します。メソッド、クラスなど。
  • バージョン2では、元の機能を置き換えることを目的としたより優れた機能を出荷します。機能にObsolete属性を設定し、「警告」に設定して、「この機能は廃止されています。代わりに、より優れた機能を使用してください。このライブラリのバージョン3では、などでリリースされます。日付、この機能の使用はエラーになります。」これで、機能のユーザーは引き続き機能を使用できますが、新しい機能を使用するようにコードを更新する時間があります。
  • バージョン3では、警告ではなくエラーになるように属性を更新し、「この機能は廃止されました。代わりに、より優れた機能を使用してください。などでリリースされる予定のこのライブラリのバージョン4では、日付、この機能がスローされます。」以前の警告に注意を怠ったユーザーは、問題を修正する方法を示す有用なメッセージを受け取ります。コードはコンパイルされないため、修正する必要があります。
  • バージョン4では、致命的な例外をスローするように機能を変更し、次のバージョンで機能が完全に削除されることを通知するメッセージを変更します。
  • バージョン5では機能を完全に削除し、ユーザーから不満が出た場合は3回のリリースサイクルで公正な警告を行いました。ユーザーは、バージョン2を強く好感していれば、いつでもバージョン2を使い続けることができます。

ここでの考え方は、影響を受けるユーザーに対して可能な限り痛みを伴わない変更を行い、ライブラリの少なくとも1つのバージョンでその機能を引き続き使用できるようにすることです。

71
Eric Lippert

「非推奨」の意味を誤解しています。非推奨とは:

使用可能であるが、通常は置き換えられているため、廃止され、最善の方法で回避されます。

オックスフォード辞書

定義により、非推奨の機能は引き続きコンパイルされます。

特定の日付の機能を削除しようとしています。それはいいです。あなたがそれを行う方法は、あなたがその日にそれを削除することです

それまでは、非推奨、廃止、またはプログラミング言語で呼ばれているものとしてマークしてください。メッセージには、削除される日付とそれを置き換えるものを含めます。これにより警告が生成され、他の開発者は新しい使用法を避け、古い使用法を可能な限り置き換える必要があることを示します。それらの開発者はそれを遵守するか無視するかのどちらかであり、誰かがそれが削除されたときにその結果に対処する必要があります。 (状況に応じて、それはあなたであるか、それを使用している開発者である可能性があります。)

26
jpmc26

すでにリリースされているソフトウェアのバージョンをサポートするために、古いバージョンのコードをビルドおよびデバッグする機能を保持する必要があることを忘れないでください。特定の日付以降にビルドを妨害することは、将来的に正当なメンテナンスやサポート作業を行うことを妨げるリスクも意味します。

また、コンパイルする前にマシンのクロックを1〜2年戻すのは簡単な回避策のようです。

「非推奨」とは、今後何かがなくなることを警告するものです。他の人がそのAPIを使用するのを強制的に禁止したい場合は、関連するコードを削除するだけ何らかのメカニズムによってコードが使用できなくなった場合、コードをコードベースに残しても意味がありません。コードを削除すると、探しているコンパイル時のチェックが得られ、簡単な回避策はありません。

編集:質問で「古い未使用のコード」を参照していると思います。コードが実際にnusedである場合、非推奨にしても意味がありません。削除してください。

12
bta

私はそのような機能を見たことがありません-特定の日付の後に有効になる注釈。

@Deprecatedでも十分です。 CIで警告をキャッチし、存在する場合はビルドの受け入れを拒否します。これにより、コンパイラーからビルドパイプラインに責任が移りますが、ステップを追加することにより、ビルドパイプラインを(半)簡単に変更できるという利点があります。

この回答しないは問題を完全に解決し(たとえば、開発者のマシンでのローカルビルドは警告は表示されますが、引き続き成功します)、CIパイプラインが設定されて実行されていることを前提としています。

6
Mael

カレンダーまたは todoリスト を探しています。

別の方法は、カスタムコンパイラの警告またはコンパイラメッセージを使用することです。ただし、コードベースに警告があったとしてもほとんどないようにします。警告が多すぎる場合は、追加の労力(約15分?)を費やす必要があり、ビルドレポートでコンパイラの警告を取得する必要があります 継続的インテグレーション は各ビルドで提供します。

コードを修正する必要があることを思い出させてください。これらのリマインダーには実際の締め切りが厳しい場合があるため、タイマーに設定する必要がある場合もあります。

目標は、問題が存在し、特定の時間枠内で修正する必要があることを継続的に人々に思い出させることです-特定の時間にビルドを中断するだけの機能は、それを行うだけでなく、その機能自体が必要な問題です与えられた時間枠内で修正されます。

4
Peter

これについて考える1つの方法は、時刻/日付の意味ですか?コンピュータはこれらの概念が何であるかを知りません:それらはどういうわけかプログラムされなければなりません。 「エポックからの秒数」というUNIX形式でrepresent回を行うのはよくあることで、OS呼び出しを介して特定の値をプログラムに送ることは一般的です。ただし、この使用法がどれほど一般的であっても、「実際の」時間ではなく、単なる論理的な表現であることを覚えておくことが重要です。

他の人が指摘したように、このメカニズムを使用して「期限」を作成した場合、別の時間にフィードしてその「期限」を破るのは簡単です。 NTP=サーバーに問い合わせるなど、より複雑なメカニズムについても同じことが言えます(独自の証明書、認証局、または暗号ライブラリにパッチを適用できるため、「安全な」接続を介しても)。最初はそのような人はあなたのメカニズムを回避するのに責任がないように見えるかもしれませんが、それはそれが自動的に正当な理由たとえば、 再現可能なビルド を用意することをお勧めします。これを支援するツールが自動的にリセット/インターセプトする場合があります非決定的システムコール。 libfaketime はまさにそれを行い、 Nix はすべてのファイルのタイムスタンプを1970-01-01 00:00:01に設定し、Qemuは 記録/再生機能 すべてのハードウェアの相互作用などを偽ります。

これは Goodhartの法則 に似ています。プログラムの動作を論理時間に依存させると、論理時間は「実際の」時間の適切な尺度ではなくなります。言い換えれば、一般的に人々はシステムクロックをいじらないでしょうが、あなたが彼らに理由を与えれば彼らはそうします。

時間には他にも論理的な表現があります。そのうちの1つはソフトウェアのバージョン(アプリまたはいくつかの依存関係)です。これは、たとえば「締め切り」の場合よりも望ましい表現です。 UNIXの時間、それはあなたが気にかけること(機能セット/ APIの変更)に固有であり、したがって、直交する懸念に踏み込む可能性が低い(たとえば、UNIXの時間をいじって、期限を回避することは、ログファイルの破壊につながる可能性があり、cronジョブ、キャッシュなど)。

他の人が言ったように、ライブラリを制御してこの変更を「プッシュ」したい場合は、機能を非推奨にする新しいバージョンをプッシュして(警告を引き起こし、消費者が使用法を見つけて更新できるようにする)、次に、完全に機能します。 (繰り返しますが)バージョンは時間の論理的な表現にすぎないため、「実際の」時間に関連している必要はないため、必要に応じてこれらを順番に公開することができます。セマンティックバージョニングは、ここで役立ちます。

代替モデルは、変更を「プル」することです。これは「プランB」に似ています。使用するアプリケーションにテストを追加し、この依存関係のバージョンが少なくとも新しい値であることを確認します。いつものように、赤/緑/リファクタリングを使用して、この変更をコードベース全体に伝えます。これは、機能が「悪い」または「間違っている」のではなく、単に「このユースケースに適合していない」場合に適しています。

「プル」アプローチに関する重要な質問は、依存バージョンが「ユニット」( of functions )としてカウントされるかどうかであり、テストに値します。または、それが実際のユニット( of functions )テストの一部としてのみ実行される「プライベート」実装の詳細であるかどうか。依存関係のバージョン間の違いが実際にアプリケーションの機能としてカウントされる場合は、テストを行います(たとえば、Pythonバージョンが> = 3.xであることを確認します)そうでない場合、しないでくださいテストを追加します(脆弱で、情報が不足し、過度に制限されるため)。ライブラリを制御している場合は、ライブラリを制御しない場合は、提供されているバージョンを使用してください。テストに合格した場合は、制限する必要はありません。合格しなかった場合は、「期限」になります。

特に依存関係を制御しない場合など、依存関係の機能の特定の使用を阻止したい場合(コードの他の部分とうまく機能しない特定の関数を呼び出すなど)には、別のアプローチがあります。コーディング標準を禁止してください/ discourageこれらの機能の使用を確認し、それらのチェックをリンターに追加します。

これらはそれぞれ異なる状況で適用されます。

3
Warbo

1つの要件は、ビルドに時間の概念を導入することです。 C、C++、またはCのようなプリプロセッサを使用するその他の言語/ビルドシステム1、ビルド時にプリプロセッサの定義を通じてタイムスタンプを導入できます:CPPFLAGS=-DTIMESTAMP()=$(date '+%s')。これは、メイクファイルで発生する可能性があります。

コードでは、そのトークンを比較し、時間が経過するとエラーを発生させます。関数マクロを使用すると、誰かがTIMESTAMPを定義していない場合をキャッチすることに注意してください。

_#if TIMESTAMP() == 0 || TIMESTAMP() > 1520616626
#   error "The time for this feature has run out, sorry"
#endif
_

あるいは、時が来たときに問題のコードを単に「定義」することもできます。だれもそれを使用しなければ、それでプログラムをコンパイルできます。たとえば、apiを定義するヘッダー「api.h」があり、一定時間後にold()を呼び出すことはできません。

_//...
void new1();
void new2();
#if TIMESTAMP() < 1520616626
   void old();
#endif
//...
_

同様の構成では、おそらく、ソースファイルからold()の関数本体が削除されます。

もちろん、これは絶対の証拠ではありません。金曜日の夜の緊急ビルドが別の場所で言及されている場合に備えて、単に古いTIMESTAMPを定義できます。しかし、それはむしろ有利だと思います。

これは明らかにlibraryが再コンパイルされた場合にのみ機能します。その後、古いコードは単にライブラリに存在しなくなります。 notクライアントコードが古いバイナリにリンクするのを防ぎます。


1

これは、パッケージまたはライブラリレベルで管理します。パッケージを制御し、その可視性を制御します。可視性を自由に撤回できます。これは大企業で内部的に見たことがあります。パッケージがオープンソースであるか、無料で使用できる場合でも、パッケージの所有権を尊重する文化においてのみ意味があります。

クライアントチームは何も変更したくないため、これは常に厄介です。特定のクライアントと協力して移行の期限について合意し、サポートを提供する可能性があるため、ホワイトリストのみのラウンドが必要になることがよくあります。

1
djechlin

Visual Studioでは、特定の日付の後にエラーをスローするビルド前スクリプトを設定できます。これはコンパイルを防ぎます。 2018年3月12日以降にエラーをスローするスクリプトを次に示します( ここから取得 ):

@ECHO OFF

SET CutOffDate=2018-03-12

REM These indexes assume %DATE% is in format:
REM   Abr MM/DD/YYYY - ex. Sun 01/25/2015
SET TodayYear=%DATE:~10,4%
SET TodayMonth=%DATE:~4,2%
SET TodayDay=%DATE:~7,2%

REM Construct today's date to be in the same format as the CutOffDate.
REM Since the format is a comparable string, it will evaluate date orders.
IF %TodayYear%-%TodayMonth%-%TodayDay% GTR %CutOffDate% (
    ECHO Today is after the cut-off date.
    REM throw an error to prevent compilation
    EXIT /B 2
) ELSE (
    ECHO Today is on or before the cut-off date.
)

このスクリプトを使用する前に、このページの他の回答を必ずお読みください。

0
user2023861