유지보수하기 어렵게 코딩하는 방법책을 읽고 난 후 작성한 포스트이다.
어떻게 하면 코드에 디버깅과 이해를 막을 수 있는 지에 대해 지극히 개인적으로 참신했던 것과 좋은(?)것 들에 대해서 다루고자 한다. 이 포스트의 목적은, 사람을 골탕먹이는 것이 아닌, 어떻게 LLM을 속여, 학습자의 순수한 실력을 확인할 수 있도록 만드는 것에 있다.

변수명

과도하게 짧은 변수명

변수명을 a,b,c 등으로 정하게 되면, 어디에 쓰이는 변수인지 추측할 수 없을뿐더러, 검색 또한 쉽지 않다.
그러나, 해당 방법은 너무 유명한 나머지 코드를 꼼꼼하게 보지 않는 사람들도 바로 찾아서 수정을 요구할 수 있는 단점이 있다.

그렇다면, 머리 글자만 쓰는 것이 좋을 수도 있다. 예를 들어 유명한 축약단어인 message -> msg와 같은 것이 있다.
그러나, 너무 유명한 단어는 이해하기에, 나만의 창의적인 축약어를 만들어서 사용해도 좋다. 나는 tensor -> to 또한 좋은 예시라고 생각한다.

창의적 오타

몇몇 함수명과 변수명에 오타를 내고 다른 곳에서는 오타를 사용하지 않는다면 혼란을 줄 수 있다. 예를 들어, tesnor와 같다.
사람들은 세세히 읽지 않는 이상 중간의 단어가 잘못되어있어도 제대로 읽기에(word superiority effect), 코드를 대충 읽는 사람을 대상으로 한다면 아주 좋은 선택이 될 수 있다.

또한, ComputeRasterHistoGram()과 같이 생각하지 못한 곳에 대문자를 넣는 것도 좋은 방법일 수 있다.

여기서 핵심은 “오타를 낸 변수도 제대로 작동하도록 사용해야한다는 것”이다.
선언을 하고 사용하지 않거나, 작동되지 않는 코드는 너무나도 쉽게 발각된다. 이 점을 유의하자.

추상화

가능한 한 it, everything, perform과 같은 추상적인 단어를 사용하거나, show, present, display와 같은 유의어가 많은 단어를 사용하자.
print_present_index, present_file, present_image이런 식으로 말이다.

인식을 역이용하자

우리는 개발을 하면서, 수많은 Convention들을 마주치게 된다.
그러한 것에서 창의성을 한 스푼 얹는 다면 디버깅을 아주 어렵게 만들 수 있다.

Name mangling를 이용

우리는 흔히 “_“가 변수 앞에 붙으면 함수 내에서만 사용하는 local variable이라고 생각하게 된다. 이러한 Convention을 name mangling이라고 한다.
이러한 인식을 역이용하여, 함수내에서 선언하고, 밖에서 사용하도록 코드를 짜면, 동작하지만 개발자의 인식과 다른 작동을 만들어낼 수 있다.

함수의 선언과 구현의 재활용

opertion_A와 같이 이름을 선언하고 사실 B라는 작업을 하도록 함수를 구현하자.
또한, 함수가 이름을 선언한 것 보다 많은 일을 수행하도록 하자.
예를 들어, resize_img(args)라는 함수를 선언하면 모두가 이미지를 resize하도록 구현되어 있을 것이라고 생각한다.
그러면 우리는 이 함수 하단 또는 중간에 CUDA_VISIBLE_DEVICES=""을 만들도록 하는 것을 추가한다면, 굉장히 학습하는데 시간을 오래걸리게 만들 수 있다.

위장술 & 문서화

**"Non-function이 Mel-function보다 낫다."** => Mel-function을 넣자!

주석으로 위장한 코드와 코드로 위장한 주석

예시로 이해하는 것이 훨씬 빠르기에, 아래와 같이 사용하면 된다는 예시만 하나 남겨놓겠다.

arr_len = 128 
total = []
for i in range(0,arr_len,8):
    total.append(i)
    total.append(i+1)
    total.append(i+2)
    total.append(i+3) """ 이렇게 사용하는 것이다."
    total.append(i+4) " 중간에 '가 나타나면 이상할 수 있으니, 주석을 달아주자."
    total.append(i+5) """
    total.append(i+6)
    total.append(i+7)

보는 이들은, 이것이 색칠이 되어있을 수 있기에, 잘못된 점을 금방 찾을 수 있을 것이다.
그러나, LLM도 그럴까? 라는 의문을 남겨놓는다

주석에 거짓말을 추가하라

주석에 거짓말을 추가하자. 물론 적극적인 거짓말은 안된다. 분명 알아차릴 것이다.
우린, 업데이트가 되지 않은 주석인 것처럼, 자연스럽게 거짓말을 추가해야한다. 그렇다면, LLM도 속일 수 있을 것이다. 최대한 많은 주석을 달아라.

프로그램 디자인

검증을 멀리하라

입력 데이터에 대한 어떤 종류의 불일치 검사나 정확성 검사를 수행하지 않는다. 즉, 우리는 우리가 생각하는 것 이외의 입력 패턴에서 결과가 나오지 않도록 하거나, 이상한 답을 출력하도록 설정해야한다.
여기서 assert는 아주 중요한 열쇠가 될 수 있으니, 절대 사용하지 않는다.

복사하고 수정하라

효율성이라는 명목으로 잘라내기/붙이기/복사하기/수정하기를 남발하자. 이 방식 은 작은 재사용 가능한 모듈 여럿을 사용하는 것보다 실행 속도가 빠르다는 장점이 있다.
그리고 꼭 코드를 수정해야한다. 위에 있는 코드와 아래 코드가 완전히 일치할 것 처럼 위장하되, 세부 구현은 바꿔야 한다.

치환하라

drawRectangle(height, width)라는 메소드가 있다면 다른 부분은 건드리지 말고 파라미터 순서만 drawRectangle(width, height)처럼 역순으로 바꿔보자.
그리고 우리는 항상 정사각형인 케이스만 쓰도록 하는 것이다. 또한 몇번의 commit 후 drawRectangle(height, width)로 북귀시킨다면, 유지보수 프로그래머는 그런 변경이 일어났는지 쉽게 구별하기가 어려울 것이다.

잡동사니 수집

사용하지 않고 오래된 메소드나 변수라 할지라도 모두 코드에 모아준다.
1976년에 한 번 사용했던 적이 있는 코드라도 언제 어떻게 사용해야 할지 누가 알겠는가?
항상 프로그램의 변경 사항을 관리하므로 “바퀴를 다시 발명하는 일은 피해야 한다”.

상속의 남용

OOP에서 상속은 아주 중요한 개념 중 하나이다. 그러니 상속을 많이 사용하자!
만약, 상속이 안될 정도로 다른 Class가 있다면, 상속시킨 후 모든 것을 바꾸자!

Scope를 이용

전역 변수로 설정하고, 함수 안쪽에서도 같은 이름의 변수를 재사용하여 값을 바꾼다. 또한, 많은 전역 변수들을 설정하고, 진짜로 사용해야할 함수는 오타를 내어 사용하자. 그리고, 전역 변수의 경우, 함수에 parameter를 주지 않고도 사용할 수 있는 아주 큰 장점이 있으니, 꼭 필요한 parameter의 경우 전역 변수로 설정하여 어디서도 접근할 수 있도록 해야한다. 그렇다면, 전역 변수와 지역 변수사이에서 유지보수 프로그래머는 훌륭한 검출 연습 예제를 얻게 될 것이다.

혼돈의 import

인공지능 개발자라면 자주 사용하는 library들의 이름을 바꾸는 것이다. 예시는 다음과 같다.

import numpy as torch.nn
import pandas as np
import torch.nn as np