日々常々

ふつうのプログラマがあたりまえにしたいこと。

リファクタリングに関する何か

リファクタリングの話をするとき、焦点が合ってないなーと感じることがたまにあるのでざっくり描いてみた。

f:id:irof:20200811115944p:plain

自分のために描いたものなので、なんか違うなーって思ったらご自身で描いてみるといいと思います。レッツモデリング

破線は依存、実線は変換。長方形は名前などで明確に識別可能なもの、角丸は様々なものを包含する活動。雲は思いです。

描いた時の経緯と言うか

f:id:irof:20200809211246p:plain

該当ツイート: リファクタリングって常時やるものなんですよね。もちろん「よーしやるぞー」って感じで行うものもあるんですけど、それは深呼吸的な。

とは言え。やったことがない、やってはいけない文化(動いているコードに触ってはいけない)に染められてしまっている、そのような方に「無意識にやれ!」と言っても、何の意味もないので言いません。むしろ害悪ですらある。

f:id:irof:20200809211308p:plain

該当ツイート: 無意識にやるようになって、ようやく「リファクタリング」がカタログ化される前の「偉大な習慣」に入門したあたりになると思ったり。 習慣にするまでは意識してやると思うんですが、習慣になったら意識しなくなるかなって。そんな感じ。

ポイント

偉大な習慣が「リファクタリング(活動)」で、しっかり定義されていないふわっとしたものなので角丸で示してます。 その中から言葉をつけてカタログ化された「リファクタリング(テクニック)」が提供する焦点はいくつかあって。

  • 名前付けによる識別
  • コードの不吉な臭いに依存する、正当化による後押し
    • これがあるおかげで「何となくキモチワルイ」を変えて良くなります
  • 外的振る舞いを守る検証方法に依存する、手順
    • これがあるおかげで「変えても大丈夫」って言えるようになります

で、リファクタリングでたまに微妙な方に行くのが「検証方法」の焦点が合ってないところ。 「外部的振る舞い」ってなんやねんってやつですね。

書籍「リファクタリング」では堅実なテストは事前条件とされています。そして自動テスト前提で書かれています。

これはもちろん(定義者が言っているからって理由を抜いても)正しいのですが、かといって「自動テストがないものはリファクタリングと呼ばない」と強固に主張したところで、それはそれで重要な場面もあるんですが、多くの場面では意味のない話です。「じゃあリストラクチャリング(もしくは他の何かしらの造語)でいいです」とか言葉を変えることにあまり意味はないですし。

と言う事で、検証方法にいろんなパターンがあるとしています。「コンパイルエラーにならない」が保てている限り安全と断言できる変更はあるわけですから。 私が重視するのは「このリファクタリングではこの検証方法を使っている」が示せる事です。 その検証方法の精度は二の次。まずはそれを識別できていること。 識別できさえすれば、その検証方法の妥当性は考えられますし、精度をあげることもできます。 検証方法が思い至らないのであれば、それは流石にリファクタリングじゃないかなって。

まとめと参考書籍(2020-08-10T12:00 追記)

リファクタリングに取り組むと「外部的振る舞い」の扱いに悩むかと思います。 自動テストがあると言っても、それで十分に保つべき外部的振る舞いが検証できてるか確信が持てないこともあるでしょう。

「この外部的振る舞いを保たねばならない」と言う唯一絶対の正解はありません。 重要なのは 保つべきものを見極め、それを検証できる形にしてから取り組むこと です。 これを保てるならば変更していい。そう言う拠り所を作れば、コードをみたときに感じるモヤモヤ(図の気になること)に向き合えます。 リファクタリングは「なんか気になる」などの感情を押し殺さず、むしろこの感性を育み、良いコードを書けるようになる最短ルートだと思っています。

コードの変更は多くのところに波及します。影響を及ぼさないように設計していれば、簡単な検証で賄えるかもしれません。 安定依存の法則に従ったモジュール設計を行っていればより達成しやすくなります。この辺りの話はClean Architectureが参考になります。

(同心円な図がよく挙げられる本ですが、本書の主眼はあの図ではありません。他のCleanシリーズと同様、考え方の本です。)

様々な責務が複雑に絡み合っていれば、包括的で堅牢な自動テストがないと厳しいかもしれません。 そう言うときは、レガシーコード改善ガイドが参考になると思います。

リファクタリングは誰かの許可を貰ってやる物ではありません。できる裏付けを自身で構築してから取り組む物です。 自分で「変えても大丈夫」と言えず、なぜコードを変えられようか。

あと言っておきたいこと

f:id:irof:20200809222051p:plain

該当ツイート: 日常的にやっているからこそ、重要なときにもできるんです。緊急時の規律。

システムの根幹をリファクタリングしたければ、呼吸のようにリファクタリングするようになっておきましょう。 素振りの習慣もないのにいきなりフルスイングしても、色々壊すんですよ。打ちっぱなしでゴルフクラブすっぽ抜けて思いっきり投げた私が言うんだから間違いない。(ちなみに小学生の頃。いまだにトラウマ。)

あ、呼吸のようにやるようになると、コミットに紛れ込みやすくなります。細かくコミットする習慣とどちらが勝るかの勝負になってきます。これどっちがいいとかも文脈依存で、どこに価値を見出すかと言う話です。これはこれでいつか掘り下げたい。

AWS CDKをTypeScriptでVSCodeで書いてる

馴染みのない環境でやると色々微妙な詰まり方しますよね。

AWSはマネジメントコンソールで作ると色々勝手にやってくれてとても便利です。 勝手にやってくれて動きはするんだけれど、何が作られてるか良く分からない状況になってしまう私。 「動くからいいじゃん」っていうのもあるかと思いますが、プロダクションなので動くのは当たり前……というか話題にする必要もありません。

動いちゃいけないものは動いちゃいけないのがすごく大事だと思っています。とても大事。動くものは動く、その上で動いちゃいけないものが動かない。 無邪気に「動けばいい」と発言される方とチームをご一緒する時にまず押さえるところにしています。 ということで何が作られているかは把握したい。

インフラわからないので最近これ読んでます。とてもおもしろい。

マネジメントコンソールも結局はAPIの操作って理解してから見たらわかるところ多いんですけど、如何せんわからないものが多すぎて、それなのに動いちゃうのが怖くて仕方なかった。 あと知らないものが勝手に作られて、それに費用がかかっちゃうのも気になるところ。

閑話休題。そんなわけで(なんで他じゃなくCDKなのかとか色々省略)AWS CDKをはじめました。 Javaでもできるみたいなんですが、ついでにTypeScriptに手を出すことにしました。TypeScriptを書くのにVisualStudio Codeを使うことにしました。これはCDKのベースがTypeScriptだったり、WorkshopがVSCodeだったからです。私にはどれも馴染みが薄いので「変える時は一つだけにしろ」の原則を思いっきり外してますが、大枠ではCDKって一つなので大丈夫(何が大丈夫なのかは知らない)。

でまぁ手探りしながらTypeScriptの文法とかも雰囲気で書いていきます。なんか動きはする。

f:id:irof:20200710155810p:plain https://twitter.com/irof/status/1280867510879784961 変なこと書いたらコンパイルエラーになるのはとても安心感がある。

あ、冒頭と反するように見えますが、ここは「動けばいい」を適用していい領域です。なぜならCloudFormationのテンプレートができて、作られるAWSリソースを検証できるから。 jestでテストできるし。テスト対象がCloudFormationのテンプレートだったりするので、CloudFormationの知識は前提になります。 CloudFormation書くの辛くなってCDKに手を出して、付け焼き刃程度とはいえCloudFormationの知識があるのが良い方に働いてはいるけれど、結局CloudFormationから逃れられないのか感があったり。 ともかく動くのは絶対的に正しいですが、「動けばいい」は取扱注意です。

そんなこんなで書いていきます。そのうち一つのファイルに長々書くのは趣味じゃないから、構造化したくなり……するとこんな感じで辛くなってきました。

f:id:irof:20200710153742p:plain

1つのTypeScriptファイル xxx.ts に対して xxx.d.tsxxx.js の2ファイルが npm run buildwatch だけど)で作られ、それが同じディレクトリに出力されます。

TypeScriptの文化なのかCDKが推奨してるだけなのかは知らないけれど、Workshop通りに作ったらこんな設定。 cdk init で作られる .gitignore では無視されるのだけどね…… Javaだと bin/ とか out/ とか build/ とか、とにかく別ディレクトリにできるのでちょっと違和感を覚えつつ、文化は自分に馴染ませてから評価することにしてるのでそのまま。

でも流石にこのファイルの中身は見ないなーと数日書いてて思ったので( cdk.out/*.template.json は結構見る)、やっぱビルド先のディレクトリ変えるかなぁもしくは消す方法はないかなぁとVSCodeの設定を眺めてたらあったた。.vscode/settings.json に以下を書く。

{
    "files.exclude": {
        "**/*.d.ts": {"when": "$(basename).ts"},
        "**/*.js": {"when": "$(basename).ts"}
    }
}

basename を指定できるのが面白いなと思いました。

f:id:irof:20200710161843p:plain

生きやすくなった。

ってとこまで至ってからこの内容で検索したら、どうもTypeScriptな方々は当たり前にやるものなようで。知らないものは目に入っても認識できなくて、知ったら目に入った時に認識できるようになるパターン。 馴染みのない環境って面白いなぁと改めて思いました。

今は xxx.tsyyy.ts に名前を変えた時に以前作られた xxx.js とかを消す方法( gradle clean みたいなの)が欲しいなと思いながら、別に何も調べてないところです。

RDRAのダイアグラムの位置付け

RDRA をやってて思うところ。

RDRAでは多くのダイアグラムが定義されていますが、これらのダイアグラムはきっと成果物(最終的に作るものの中核にあるものをこう呼んでみる)では無いです。多分ハマりどころ。 システムコンテキスト図も業務フロー図もユースケース複合図も、どれもRDRAのエディタとビューアでしかありません。中間成果物、もしくは補足資料的な位置付け。

じゃあ成果物が何かって言うと、各アイコンと関連線。詰まるところ全てのアイコンと関連線を持ったリポジトリそのものが成果物なんだと思います。

ダイアグラムを通してリポジトリを読み書きしている感覚。アイコンを見つけやすい、関連線を引きやすいダイアグラムを使ってアイコンを配置し、アイコン間に関連線を引く。その整合性を確認しやすいダイアグラムを使って、アイコンと関連線を検証する。各ダイアグラムはそのためのものでしか無い。 決して「ダイアグラムを作ればヨシ!」と言うものでは無い。

……とか思い至ってからRDRA2.0ハンドブックを読み直したら「はじめに」に書いてた件。

RDRAではダイアグラムよりもモデル要素間のつながりを重視します。

紙版で持っています。なんだかんだで物理媒体便利。画面もCPUも占有しないし電気も使わない。やはり石板が最強か。

やはり重要なことは最初に書いてますね。そして理解していないと読んだつもりでも重要さを読み落としてしまう……。

どうしてもRDRAはダイアグラムが目立つようで、「どのダイアグラムを作ればよいか」「どのダイアグラムに何を書けばいいか」と言った疑問をよく聞きます。私も使いこなせていない感はあるのでなんともなのですが、少なくとも早めにダイアグラムを主人公じゃなくす方にシフトした方がいいんじゃないかなーと思ったりしています。

ダイアグラムの見方が変われば、ビジネスユースケース図を作るか否かとか、ビジネスユースケースから業務フローを作るべきか、利用シーンが必要か、いきなりユースケース複合図に行っていいのかダメなのか、なんてことはどれでも良くなってきます。アイコンと関連を書きやすいかどうか、書き始めてからでも。なんか足りないなって思ったら他のダイアグラムを使ってみたらいい。「ユースケース複合図 業務フロー付き」とかがあるのも、それの方が書きやすいからだと思う。一つ一つのダイアグラムにそんな価値はない。と言うか、一つのダイアグラムだけ取り出すと、ほとんど価値がない……。

ダイアグラムに目が向いてしまうと、RDRAが避けようとしている「ドキュメントを作ることが目的になっている」に結局ハマりそうだなぁと。 でもダイアグラムをある程度書き慣れないと厳しい気もする。

RDRAのダイアグラムはシンプルでぱっと見わかりやすいのですが、書かないと読めるようにならないんじゃないかと。少なくとも書いている場に同席している必要があると言うか、同席できる程度の記述量だからRDRAなんだろうなって思ったり。

こんなことをふわっと考えている今日この頃。

モデルベース要件定義テクニック

モデルベース要件定義テクニック

RDRA1.0ですが、2.0ハンドブックと比べると変わった点、強調したい点、変わらない点が見えるので面白いです。ページ数が多い分、RDRA自体のコンテキストはこちらからのほうが読み取れる気もする。