私はNULLの問題について書き続けていますが、毎日ニュースが私に思い出します:NULLはまだ生きています。
TL;DR: 適切な検証および null-safe パターンを使用することで、ランタイム障害を引き起こす NULL 参照を避ける
TL;DR: 適切な検証および null-safe パターンを使用することで、ランタイム障害を引き起こす NULL 参照を避ける
トラブル
- ランタイムクラッシュ
- 大規模な事故と中断
- 予想外の行動
- ハードデバッグ
- ユーザの不満
- システム不安定
- 信頼性の低下
インGoogle クラウドケース:
- 悪質なエラー処理: Null データを優雅に処理する代わりにコードが崩壊
- No feature flags: New code was not gradually rolled out with safety controls. セキュリティコントロールで新しいコードが徐々にリリースされなかった。
- Instant Global Replication: Bad Data Spreads Worldwide Immediately Like in Crowdstrike Incident (インスタントグローバル・レプリケーション:悪データがCrowdstrike Incidentのように世界中にすぐに広がる)
- No Randomized Backoff: Recovery Caused Infrastructure Overload (ランダム化されたバックコフなし)
- 不十分なテスト: デプロイ中に失敗シナリオがテストされたことはありません。
ソリューション↓
- ゼロを避ける
- null チェックを使用して、null があなたのコントロール外にある場合(例えば、外部 API)
- デフォルト値の初期化
- ガード条項の適用
- ゼロオブジェクトの使用
- オプションを使うな
改装 ️
https://hackernoon.com/code-refactoring-tips-no-015-remove-null
コンテキスト
平成25年6月12日 A大切な中断Google Cloud プラットフォームで実現しました。
Google CloudおよびGoogle Workspaceサービスの数十件に影響を及ぼし、世界中で午前10時49分から午後1時49分まで(合計3時間)、一部のサービスは完全に回復するのに時間がかかる。
この障害は、Google の API マネジメントシステムのカスカディング エラーによって引き起こされました。
- トリガー :
2025年5月29日、Googleは「Service Control」(API管理システム)に新しいコードを展開し、追加の割引ポリシーチェックを追加しました。
このコードには重大な欠陥があり、適切な欠陥がありました。ミス操作保護されていなかったのは、フラッグ Flag.
- 失敗:
6月12日(日) 空白を含む政策変更ゼロService Control がこれらの空白のフィールドを処理しようとすると、保護されていないコードパスに null ポインタに遭遇し、バイナリが無限のループで崩壊します。
- グローバルな影響:
配分管理はグローバルであるため、この腐敗したデータは数秒で世界中で複製され、サービスコントロールがあらゆる地域で崩壊した。
ゼロポインタの例外は、存在しないオブジェクトの方法または属性にアクセスしようとすると発生します。
これは、変数が有効なオブジェクトインスタンスの代わりに null 参照を含んでいる場合に起こります。
この問題は、これらの例外がアプリケーションを崩壊させ、ユーザーを失望させることができる生産環境において特に危険になります。
Java、C#、JavaScriptなどの言語は特にこの問題に敏感ですが、現代の言語機能やパターンはこれらのクラッシュを完全に回避するのに役立ちます。
Nullsは何十年もの間、ソフトウェア業界で大きな問題となっていますが、ソフトウェアエンジニアは、開発者の警告にもかかわらず、それらを無視し続けています。
サンプルコード
Wrong ❌
public class ServiceControlPolicy {
private SpannerDatabase spannerDB;
private QuotaManager quotaManager;
public void applyPolicyChange(PolicyChange change) {
// NULL POINTER: change can be null
Policy policy = spannerDB.getPolicy(change.getPolicyId());
// NULL POINTER: policy can be null from the database
String quotaField = policy.getQuotaField();
// NULL POINTER: quotaField can be null (blank field)
quotaManager.updateQuota(quotaField, change.getValue());
}
public void exerciseQuotaChecks(String region) {
// NULL POINTER: policies list can be null
List<Policy> policies = spannerDB.getPoliciesForRegion(region);
for (Policy policy : policies) {
// NULL POINTER: individual policy can be null
String quotaValue = policy.getQuotaField();
// NULL POINTER: quotaValue can be null before trim()
quotaManager.checkQuota(quotaValue.trim());
}
}
public boolean validatePolicyData(Policy policy) {
// NULL POINTER: policy parameter can be null
String quotaField = policy.getQuotaField();
// NULL POINTER: quotaField can be null before length()
return quotaField.length() > 0 &&
!quotaField.equals("null");
}
public void replicateGlobally(PolicyChange change) {
List<String> regions = getGlobalRegions();
for (String region : regions) {
// NULL POINTER: change.getPolicy() can return null
spannerDB.insertPolicy(region, change.getPolicy());
}
}
}
正しい
public class ServiceControlPolicy {
private SpannerDatabase spannerDB;
private QuotaManager quotaManager;
public void applyPolicyChange(PolicyChange change) {
if (change == null) {
// Assuming it comes from an external API
// Beyond your control
change = new NullPolicyChange();
}
Policy policy = findPolicyOrNull(change.policyId());
String quotaField = policy.quotaField();
if (!quotaField.isEmpty()) {
quotaManager.updateQuota(quotaField, change.value());
}
}
public void exerciseQuotaChecks(String region) {
if (region == null || region.isEmpty()) {
// Assuming it comes from an external API
// Beyond your control
return;
}
List<Policy> policies = policiesOrEmpty(region);
for (Policy policy : policies) {
String quotaValue = policy.quotaField();
if (!quotaValue.isEmpty()) {
quotaManager.checkQuota(quotaValue.trim());
}
}
}
public boolean validatePolicyData(Policy policy) {
if (policy == null) {
// Assuming it comes from an external API
// Beyond your control
// From now on, you wrap it
policy = new NullPolicy();
}
String quotaField = policy.quotaField();
return quotaField.length() > 0;
}
public void replicateGlobally(PolicyChange change) {
if (change == null) {
// Assuming it comes from an external API
// Beyond your control
// From now on, you wrap it
change = new NullPolicyChange();
}
Policy policy = change.policy();
if (policy == null) {
// Assuming it comes from an external API
// Beyond your control
// From now on, you wrap it
policy = new NullPolicy();
}
List<String> regions = globalRegions();
for (String region : regions) {
spannerDB.insertPolicy(region, policy);
}
}
private Policy findPolicyOrNull(String policyId) {
Policy policy = spannerDB.policy(policyId);
return policy != null ? policy : new NullPolicy();
}
private List<Policy> policiesOrEmpty(String region) {
List<Policy> policies = spannerDB.policiesForRegion(region);
if (policies == null) {
// This is a good NullObject
return Collections.emptyList();
}
return policies.stream()
.map(p -> p != null ? p : new NullPolicy())
.collect(Collectors.toList());
}
}
class NullPolicy extends Policy {
@Override
public String quotaField() { return ""; }
@Override
public String policyId() { return "unknown-policy"; }
@Override
public Map<String, String> metadata() {
return Collections.emptyMap();
}
}
class NullPolicyChange extends PolicyChange {
@Override
public String policyId() { return ""; }
@Override
public String value() { return ""; }
@Override
public Policy policy() { return new NullPolicy(); }
}
検出
- (x)半自動
Null チェックなしのオブジェクトで直接メソッド呼び出しのコードをレビューすることにより、潜在的な null ポインター例外を検出できます。
Linters は、返す可能性のある方法から返す値を調べることができます。ゼロ初期化されていないオブジェクトフィールドを検索し、潜在的な null dereferences を表示する静的分析ツールを使用します。
現代のアイデアはしばしば警告でこれらの問題を強調します。
タグ ️
- ゼロ
レベル
- (x) 中間
なんでバッテリーが大事なの️
IN THE現実世界物体は存在するか、存在しないか。
あなたのプログラムでこれを正しくモデリングすると、あなたは明確なOne-to-one メール現実とコードの間
null 参照を許可することによってこのバイジェクションを破ると、あなたのコードに存在するが、現実世界には存在しない幽霊オブジェクトが生成され、これらの存在しないエンティティとの相互作用を試みるときに崩壊をもたらします。
ライセンスプレートを「NULL」と名付けると、駐車券が多い
世代
AI ジェネレーターは、ハッピー パス シナリオに焦点を当てているため、ゼロ ポインタの脆弱性を持つコードを頻繁に作成します。
彼らはしばしば、オブジェクトが存在する可能性のあるエッジケースを考慮せずに方法呼び出しを生成する。ゼロ特に、複雑なオブジェクトの階層や、外部データソースに対処する場合。
検出 ↓↓
AI ツールは、防御プログラミングの実践に関する明確な指示を提供する場合に、ゼロポインタの問題を検出し、修正することができます。
ぜひお試しください!↓↓
注意:AIアシスタントは多くの間違いを犯す
Suggested Prompt: すべての Null References を削除する
Suggested Prompt: すべての Null References を削除する
Without Proper Instructions |
With Specific Instructions |
---|---|
結論 ↓
ゼロポインタ例外は、プログラミングにおける最も一般的なランタイムエラーの1つです。
適切な null チェックを実装し、Null Object デザインパターンを使用し、防御プログラミングの実践を採用することで、これらのトラブルのほとんどを削除できます。
検証コードの小さなオーバーヘッドは、アプリケーションの安定性とユーザーエクスペリエンスで大幅に報われる。
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-iii-t7h3zkv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xliii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxvi
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xlii
詳細情報↓↓
ディスカレーター
Code Smells Are Mine(コードの匂いは私のもの)意見.
私はそれを私の10億ドルの間違いと呼びます.それは1965年にゼロ参照の発明でした。
私はそれを私の10億ドルの間違いと呼びます.それは1965年にゼロ参照の発明でした。
トニー・ホア
この記事は CodeSmell シリーズの一部です。