はてなキーワード: リファクタリングとは
いやぁ〜、テキストエディタの世界、めっちゃディープでんねん!聞いてくださいよ〜。
まず、テキストエディタの心臓部、バッファ管理システムについてや。これ、単なるテキスト保持やないんですわ。例えば、Emacsのガベージコレクション機構。マーク&スイープ方式採用してて、バッファ内のLispオブジェクトを効率的に管理してんねん。これがあるから、長時間の編集作業でもメモリリークせーへんのや。
次に、レンダリングエンジン。これが曲者でんねん。Unicode標準のUAX #9に準拠した双方向アルゴリズム実装せなアカン。さらに、合字処理のためにOpenTypeのGSUB/GPOSテーブル解析も必要や。Harfbuzzライブラリ使うんやけど、カスタムシェーピングエンジン組み込んで、特殊な文字体系にも対応せなアカンのや。
構文解析エンジンも侮れまへんで。LR(1)パーサーじゃ複雑な言語構文に対応でけへんから、GLR(Generalized LR)パーサー実装するんや。これで曖昧な文法も扱えるようになるんですわ。Treesitterライブラリ使うと、インクリメンタルな構文解析ができて、巨大ファイルでもリアルタイムにハイライティングできるんや。
差分アルゴリズムも奥が深いんですわ。Myers差分アルゴリズムだけやなくて、Histogram差分アルゴリズムも実装せなアカン。大規模リファクタリングの差分表示に効くねん。さらに、セマンティック差分アルゴリズムも組み込んで、構造的な変更も検出できるようにするんや。
非同期処理システムもめっちゃ重要や。単なるPromiseやasync/awaitやのうて、Reactive Extensionsベースのストリーム処理実装するんや。これで、複雑なイベントシーケンスも扱えるようになるんですわ。さらに、アクターモデルベースの並行処理システム組み込んで、マルチコア活用した並列処理も可能にするんや。
最新トレンドもめっちゃアツいんですわ。例えば、Language Server Protocolの拡張や。単なる静的解析やのうて、シンボリックAI使うた意味解析まで可能にしてるんや。これで、コードの意図を理解して、より高度なリファクタリング提案ができるようになるんですわ。
WebAssembly統合も進化してるんや。Single Instruction, Multiple Data (SIMD)命令セットサポートで、テキスト処理のパフォーマンスが爆上がりしてんねん。さらに、WebAssembly System Interface (WASI)採用で、ファイルシステムアクセスも可能になってるんや。
AI支援機能も侮れまへんで。単なる補完やのうて、プログラム合成(Program Synthesis)技術導入してるんや。部分的な仕様から完全なコードを生成できるようになってんねん。さらに、説明生成AI組み込んで、生成されたコードの詳細な解説までしてくれるんですわ。
リアルタイムコラボレーションも進化してるんや。Conflict-free Replicated Data Type (CRDT)のカスタム実装で、ネットワーク遅延があっても一貫性保てるようになってんねん。さらに、意図ベースの競合解決アルゴリズム導入して、複雑な編集操作の衝突も自動解決できるようになってるんや。
拡張性アーキテクチャもすごいんですわ。WebAssemblyベースのプラグインシステム採用して、言語に依存せんプラグイン開発可能になってんねん。さらに、サンドボックス化されたランタイム環境提供して、セキュアなプラグイン実行も実現してるんや。
性能評価も厳しくなってるんですわ。起動時間は、コールドスタートだけやのうて、ホットスタートも測定せなアカン。メモリ使用量も、物理メモリだけやなくて、仮想メモリの使用状況も追跡するんや。CPU使用率は、マイクロアーキテクチャレベルの最適化まで求められるようになってんねん。レンダリング性能は、GPUアクセラレーションの効率も評価せなアカンのや。応答性は、入力レイテンシだけやのうて、知覚的な応答性(Perceived Responsiveness)も測定するんですわ。
いや〜、テキストエディタの世界、マジでディープすぎて、もう頭おかしなるで〜!こんな感じで、テキストエディタの最深部まで潜ってみましたけど、いかがでしたか?テキストエディタ、侮れまへんで〜。ホンマに。
端的に言えば、「やり方が変わったらミスが増えるし覚えるまでの仕事が遅くなるから」以外にないですよ。
新しいやり方になって大幅スピードアップならともかく、仕事のスピード自体はおおむね一緒だけどやり方だけは変わるってパターンの場合は現場の負担が半端ない。
現場が死ぬ気でやれば解決するわけでもなく、人員を一時的に増やした所で初心者ばかり集めては結局ミスが増える。
「リファクタリングの方が安く仕上がりると勘違いしてるから目指してるんだろうな」と思ってる人、ただのバカです。
発注側も一次請けも現場もみんな「いっそ根本から作り直したほうがシステム屋さん的には楽なんだろう」ってことは分かってる。
でも作り直してしまった場合に生じる現場の混乱とリファクタリングのコストを天秤にかけて現場のやり方を変えないことを選んでるわけ。
そこが分かってないで「まーたアホな仕様でシステム更新しようとしたんでしょー。学習しないね―」みたいなこと言ってるのいい加減見飽きたよ。
去年から稼働している現場で、以前からあったReact Nativeの面倒を見ているんだがまあこれがひどい出来なんだ。
jQuery時代に見かけたようなコードをやたら見かけたので思わず懐かしくなってしまった。
リファクタリングしようとしたけど直す範囲が広すぎてアプリを壊しかねなかったので、早々に諦めてだましだまし保守をしていた。
そんな中今年に入ってアプリのリニューアルの話が出てきた。React Native捨ててSwift/KotlinやらFlutterに書き換えるとかそういうのではなく、デザインの刷新といくつかの機能改修。
このままだとアプリが更に魔窟化するので、マネージャーに色々相談したところいくつかの事実がわかった。
ということだった。
結局現状のまま進めるわけにはいかず、要件定義の傍らリファクタリング作業をしている。
そういう経緯もあったので、リファクタリングとテストの工数も積んだ上で見積もりだしてもらってる。
「レガシーアーキテクチャをモダンアーキテクチャに刷新」なんてよく聞く話しだけど、
実態は「長年の増改築とだましだましのリフォームが限界になってきたので新築で建て替えます」何だと思う。
最近は「Vue.jsからRemixにマイグレーション」なんて見かけるけど、悪いのはVue.jsじゃなくて禄に設計しないでコード書いてるエンジニアと、
リファクタリングには予算でないけどマイグレーションなら予算取れるという悪しき風習。
年がら年中フロントエンド刷新しているような会社は地雷なので行かないほうがいい。
「この関数にこういうパラメータを使ったこういう処理を追加してくれ」などと言われたら、コードは複雑化するのは当然だろう。
かといってこういう要求が来た時に、コード全体を一から作り直して簡潔にしようと思うのはナンセンスだ。
コードの量にもよるが、一定程度の量のコードがそこにあるときは、やはりリファクタリングの方が効率よく進められる。
「僕はリファクタリングなんてしませぇん、一から書いた方がいいでぇす」というのは、特定の現場・状況だけにあてはまるものだと認識しておこう。
確かに「コード全体をリファクタリング」なんてしようと思ったら大変すぎるが、通常は「修正を担当する部分をついでにリファクタリングする」でOKだ。
ユニットテストさえかけていれば、そのリファクタリングによって、バグが見つかりやすくなるだろうし、保守性も上がるのである。
なお、本当にコードベースが酷いカオス状態で、ゴッドオブジェクトを使っているような状況になったら、「書き直す」という利点が少しはあるかもしれないが、そういう場合は関係各位に同意を取らなければやってはいけない。
そういったカオスな状況でさえ、平均的なプログラマーは「良いコード」よりも「慣れているコード」に愛着を持つ傾向にある。
もしあなたが「コードを綺麗にするためにすべてを一から書き直そう」と、無断でそのようなことをやったら、彼らが慣れていないという理由で批判の嵐が殺到するだろう。
プログラマーの三代美徳は、怠惰でもなければ傲慢でもない。本能、感情、混乱である。
本能とは、モチベーションの本質的部分である。エロいdeepfakeを作りたい、頭よく見られたい、金儲けしたいといった動機によってプログラマーは手を動かす。
本能がなければドーパミンも存在しない。コードを書く誘因は本能的衝動によって生み出されている。
感情とは、要するに好き嫌いのことだ。たくさんの経験を積み重ねてセンスを獲得するには、好き嫌いに敏感でなければならない。
なぜvscodeがクソで、emacsが素晴らしいのか。なぜマイクロサービスアーキテクチャに強い疑念があるのか。なぜベンダーロックインが金の浪費に繋がるのか。
そういったことは、経験から学び、そして感情という次元に落とし込まれる。感情は少数の次元で美的感覚を得るための優れたセンサーである。
混乱とは、人生である。混乱したことのないものはエントロピーを操ることはできない。
コードは常にエントロピー増大の法則に晒されている。高エントロピーの乱雑的コードを読んで混乱したことがなければ、リファクタリングもできないだろう。
誰にも相談せずに
コードを書く上で重要なことは?という質問に対して、アスペならば「実行できること」と答えるだろう。
当たり前なことしか言っていない。「実行できること」という文からは全く有益な知見を得られない。
実行できることは重要性ではなく、必要性である。重要性とは、必要なことをすべてやった上でなおやる価値のあることを意味する。
そう考えた時に私がよく思うのは「最短時間で理解可能」であることが重要であると思うわけである。
しかしここに宗教がある。そもそも、人間が物事を理解するプロセスは人それぞれである。
私は一度、関数やモジュールで適切に分離するためのリファクタリングというものを行ったことがある。
というのも、一つの関数に万を超える行が書かれていたため、上司がリファクタリングを命令したためである。
具体的詳細はprivateメソッドに、公開する必要のあるものはpublicメソッドに移した。
そして当初働いていた職場での反応はどうだったかというと、「スパゲッティコード」だというのだ。
スパゲッティコード?一つの関数に万を超える行があるほうがスパゲッティだと普通は思うだろう。
ところが、彼らの脳内では、「常にコードの詳細が見えていなければ気がすまない」という、カプセル化を無視する思想で動いていたため、関数化すると関数の最下層まで辿らないと気がすまないらしかったのである。
このようにして、教育の無い人間はコードの読み方もカプセル化も知らないので、非生産的な方法が最短の方法になってしまうのである。
コードを最短で理解するためにはどうするのか。基礎知識を教育された集団の中に身を置くのがまず先決である。
例えばcalc_monthly_salary_yen(Person p)という行が存在した時、いちいちcalc_monthly_salary_yenの中身を常に見に行くような人たちはダメだ。
「人間のデータを入力すれば円単位で月の給料を計算してくれるんだろう」とざっくりと自然言語的に読み進められる人たちでなければ「最短理解」は難しい。
開発手法でしっくり来てるのがAndy HuntさんのTracer Bullet Developmentで、開発の方向性を示すのに試作を作る方法。
1. 主要なシステム オブジェクトを定義。UI、サーバー、ロジックビット、データベース抽象化レイヤー等。
3. データフローを実現するために API とその戻り値をコーディング。
4. 単体テストを使用して API の予想される使用法を文書化。
5. 各 API に必要な量の既定のデータ (別名、ダミー データ、偽のデータ、ハードコーディングされたデータ) を追加して、API が「実行」されるようにする。
6. あらかじめ用意されたデータを実際の機能に段階的に置き換える。