島までは遠い 〜サークルアラウンド株式会社代表佐藤のブログ〜

佐藤正志@サークルアラウンド株式会社のことが少しわかる場所。プログラマーを育てるトレーナーとして、現役のソフトウェア技術者として、経営者の端くれとして、想うことをつづる。

仕事納め

12/26 が仕事最終日でした

弊社はフルフレックス制度なので、「基本的に」という前置きではありますが、昨日12月26日で業務としては終了いたしました。弊社が一次請けで責任を持っているシステムに関しては障害対応等での緊急の稼働はあり得ますがそれ以外の開発は一旦ストップしております。

ずっと継続したシステム運用をご依頼いただいているお客様に加え、新たに古い繋がりから生まれた大きなお仕事や、弊社を巣立ったメンバーと一緒の仕事が実現するなど、大変充実した一年でした。

みなさんありがとうございました。そして来年もよろしくお願いいたします。

納会にて

フルリモートメンバーなので集まって酒盛りのようなことはしていないのですが、短時間ちょっと会話するようなことは継続しています。あまりテーマなど求めずに雑談して終わることが多いですが、今年の話題はあとで振り返りたいかもしれないので少し残しておきます。

まず、知り合いに聞いたところによると いわゆる SES のJOIN条件が以前より高度になっているそうで、下手すると「現場経験5年以上」のような求め方をされるそうです。縁故で入ってしまう弊社では気付きにくいところですが、以前言われていた即戦力というのをさらに超えて、一般以上にできる人を求める傾向になっているのでしょう。

これは今後の採用にはかなり影響があると思うので、大変気になります。弊社はAIの台頭のタイミングから「一時受けのシステム運用含めた仕事」にウェイトを傾けているので、新たな人が入ってきた時にもシステム開発の現場に入りやすい土壌があります。一般のSES業者にはそういうところがないと思われるので、資金繰りの悪化が懸念されると感じました。待機社員が増えるとそのまま経営を圧迫するので、よほどの体力がないとこのタイプのSESは継続できないと思われます。

そういった世間の動きはあるとして、今後の受託システム開発においては「AIにコードを書かせ、レビューし、他のレビュアーに依頼してマージ」という流れになると思う為、これまで「コード書き」という属性だけで仕事できた人に居場所がなくなってきます。全メンバーにこの傾向は都度伝えていますが、改めて伝え直した次第です。

また、上位層のメンバーは「仕事を終わらせる」能力が求められますので、そのための見通しや根回し、並行して仕事を進めるための工夫、大ブレーキが起きないようなポイントについての考え方を改めて問答も交えながら確認しました。長く一緒に仕事している人には言葉としては根付いてきていると思われましたが、実践をより適切にやることが求められます。

少なくとも「開発に時間がかかる」という状態をAIがかなり解消してくれるため、見通しが立てやすくなったことは間違いなく、ボトルネックはほとんど人間であると思って良いでしょう。できるだけ事前にリリースまでの障壁を見通してアクションし、並行したタスク進捗をしてスムーズに完了させるのが理想です。

事前に資料など準備してなかったので、オンラインスライドにポイントを書きつつ進行しましたが、それなりの内容にたどり着けたと思います。

来年以降求められる現場の開発者像

現場の開発者として求められる姿は、AIの台頭で大きく変化したはずです。

  1. AI に適切に指示して無駄なくコードを書かせることができること
  2. 書かせたコードをレビューして「とりあえず動く」で完成されたコードを適切に直させられること
  3. 上記を低コストで行わせる、タスク分解やプロンプトの技術

これは昔あった「カチャカチャ、ターーン!」というコード書きのイメージを大きく覆しています。そしてスキルとしては現場をリードしていたメンバー(テックリードなどリーダー格が多いはず)のスキルの一部になります。

ただし、対人ではないことから「人間とのコミュニケーションが苦手」という人にもできます。だからこそ現場の開発者の基礎知識として数えられます。

ちなみに2についてが開発者個人のスキルにかなり密接になるため、設計スキルを磨くのは必要でしょう。「どこに、何が書かれているべきか」「テストでは何をチェックすべきか」などをある程度持っている必要があります(この知識をもちろんプロンプトにするのですが、適切に遂行されたかチェックできないと意味がありません)。

コードをたくさん書いた経験がなくとも、単一責任やOpen/Closed などの原則、MVC(FatControllerを避けるなど)、疎結合を避ける設計の哲学、テスト容易性など、ベストプラクティスやアンチパターンから学べることが数多くあるので、優秀なレビュアーとなれれば活躍しやすいですね。

開発者としての自分

3年くらい前から「自分が現場の開発者としてどれだけできるか改めてチャレンジしたい」と思って、自身が外部のプロジェクトにJOINする方針に切り替えていました。当時色々と知り合いに相談させてもらってプロジェクトに入れてもらいました。ありがとうございました。

しばらく社長業を中心にしていたので最初はとてもしんどかったのですが、やっている途中でAIコーディングの現場への浸透があり、とても幸運でした。自分の現場感を持った上でAIの評価をして自分なりの付き合い方を決められたのは個人・会社双方にとって大きな利益でした。

今関わっているプロジェクトでも、一応現役25年続けた経験者として尊重していただきつつ、技術難易度の高い開発を任せてもらえて幸運だと思います。そして少し変わったこととしては、たとえば私だけが仕様を理解しているが技術難易度が低いタスクについて以前は「仕様を明文化して誰かにアサイン」のところ「仕様を書いてAIにお願いして仕上げる」に変わったことです。これは先のSESの話とも通じることで「仕様を書いてくれればコード書けます」という人がいても、頼むことはないということになります。

私の場合、https://github.com/ms2sato/agent-console を作ってから複数プロジェクトの複数タスクで並行稼働は容易になったので、ほとんどいつも複数のタスクを稼働させています(今も2-3タスクが動いてます)。そのラインに乗せやすい仕事は他人に依頼する必要がなくなるのですね。先に書いたSESで求められる人材の高度化はこういう部分で納得感が高いです。

「まだまだやれる」感は掴めたので、あと5年くらいは現場の開発者でいられるだろうか。

会社Webサイトをリニューアルしました

ずっと私の作業が滞っていてアップできていなかったのですがやっと公開できました。

circlearound.co.jp

今回は Astro を使って Firebase Hosting で公開。色々やってみたけれども、Fireaseはこの手の内容とは相性が良いと思われる。PRごとのPreviewの実現が無料で手軽にできるのもすごくいい。

今後やりたいことがいくつかあるけれども、まずは一歩踏めたということで。

手でコードを書かなくなった話とgit-worktree との戦いの記録

※ Claude Codeさんに描いてもらった雰囲気の絵

以前の記事から10ヶ月くらい経って、ほとんど手でコードを書かなくなった

ms2sato.circlearound.co.jp

2月ごろにこの記事を書いていて、まだ色々と「自分が書かずにいいコードを書かせる方法」を研究していた。 今12月なので10ヶ月くらい経過したところ。もはや現場の開発でも複数のAIエージェント(主にClaude Code 4.5 Opus )に並行して指示を出しながら仕事をしていて、自分の手でコードを書くことなどほとんどなくなってしまった。依頼とレビューだけをしている。

  • 動作確認が面倒だと思っていたら Chrome Devtool MCP を入れたらなんでもどうにかなるようになっていた。
  • 1つだけ指示を出すのだと脳が空くので、基本的に複数のタスクを並行してこなしている
  • 使っている脳みそとしてはメンバーに依頼してコードをマージしている時とほとんど一緒

自分が試したところでは Claude Code Opus 4.5 は大変優秀で、融通がきき、見通しも良いアクションをしてくる。 Gemini 3がすごいというような風潮があるが、自分が試した限りだとそんなに優秀さを感じなかった。若干こだわりが強すぎて素直さのない対応をされている気がする。codexはcodex自体の思想が安全に寄りすぎているようなのとcli自体がまだまだ発展途上な感じが否めない。codex経由で使わなければいいのかもしれないがあまりその気にはならない。

git-worktree という便利な道具とDXの悪さとの戦い

CLI での戦い

git-worktree は git ブランチごとに別のディレクトリを用意して仕事ができる仕組み。AIツールを並行利用するときに大変便利な道具だ。これがあると複数のタスクを並行してこなすのが捗る。しかし、コマンドが提供する環境はとにかくDXが悪い。

「素早く新たなWorktreeを作って終わったら素早く消す」という流れが作りにくい。Worktree を作成するコマンドがどうも長くなってしまうから打つのに躊躇するのもあるが、.env などの 周辺ファイルをコピーしたり、場合によってはシステムのポートを変更したりなどしないといけないので環境を作るのに億劫になる。

とりあえずコマンドの使い勝手を向上させようとして色々と試行錯誤してみた。その成果が git-wt コマンド。

github.com

これはそこそこ上手く動いた。とにかくcliで面倒なことをどんどんやっつけてみたものだ。テンプレートディレクトリを作って、.env の内容がいい感じに変更されつつコピーされる仕組みまで作っている。顧客のリポジトリを扱う自分としてはリポジトリを汚さずにこれができるようにしていて、使い勝手がいい。利用の程度が軽いうちはこれで問題なかった。

ローカル用 Webツールを試す

同時並行に複数のプロジェクトを動かすことのある自分にとって、中途でAIを放置することはザラにある。そうするとタブを開きっぱなしで置いておくのだが、どんどんiTermのタブが増えてしまう。大抵タブは1つでは済まず、同じディレクトリで別のターミナルを開いてコマンドを打つから、タブが10を超えてくると「どれがどれやらわからん」ということになってきた。

これがAIの活動を監視するタイプの人なら問題にならないのかもしれないが、私のように放置してたまに見にくるタイプの利用ではかなりしんどくなってしまった。

そこで出会ったのが Vibe Kanban で、これは救世主になると期待した。

www.vibekanban.com

しかし、基本的にAIに全件委任させるような設定が前提のようで、私のように、許可リストを適度にコントロールしたい使い方とは相性が悪い。DockerでラップしたりするとChrome Devtools MCP が使いにくくなってしまうし、どうも面倒が増えてしまい、使いこなすことができなかった。それに若干色々と盛りすぎてて自分にはオーバースペックとなったのもある。

ローカル用 Webツールを作る

それで結局ないなら作るか、という気持ちになってやってしまった。

github.com

自分としては以下のことができれば良かった。

  1. git worktree が直感的に扱えること
  2. 環境構築用のファイルがテンプレート的に用意できてディレクトリごとに違う内容でコピーできる
  3. 同じ Worktree で動かしているシェルがひとまとまりになる
  4. 各AIのCLIの動作を妨げず、CLIのまま使える

Claude Code 専用、 macOS 専用でとりあえず割り切ってゴリゴリ Claude に書かせていたらかなり自分にとっては使いやすいものになってきた。社内の勉強会で紹介してみたらメンバーの1人が気に入ってくれたので、自分としてはかなり満足している。

その後、追加のエージェントの機能なども作ったが、それはあまり利用しておらず、今のところはこれで Worktree を開いては閉じすればいい気持ちになってきた。簡易的な git diff 機能も作っておいたが、まだバグ入りなもののそこそこ役に立つ。

そうして何より驚くのが、この手のツールを作る時間コストの少なさ。とにかくすごいスピードでCloud Code が書いていくので自分の気力が尽きる前に一定の成果に辿り着けた。これは本当にすごい。

特にオチはないので一旦また中途の報告としてこれを置いておく。また何ヶ月かしたら続きを書く。

Roo Code をいい感じにカスタマイズする過程のメモ

はじめに

2025/2/10 から Anthoripic Claude 3.5 Sonnet に課金をして、 2025/2/15 くらいまで業務の傍だったり、休み中のダラダラ時間だったりの中でガリガリ使い込んで得た知見を一旦ここに書いておく。まだ途中だし、自分なりにやった結果なので誤っているかもしれないが、生の体験を記す。

XにちょいちょいPOSTしてたので、生の内容はこの辺にも

https://x.com/search?q=%20from%3Ams2sato%20%23%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%20since%3A2025-02-10%20until%3A2025-02-16%20&src=typed_query&f=top

基本はタスク実行後の振り返り

Roo Code は .clinerules ファイルを適切に書いていくことでスムーズに作業が行える仕組みがあり、このファイルについての適切なデフォルトだったり、内容の知見を持つことが最初にいい感じにチームで使えるポイントだと考えた。そしてその育成作業は誰かが一人で最初にやってしまえばよく、つまり私の仕事とした。

タスクを行った後に以下の観点で振り返りをさせている

  • 時間がかかった箇所の特定と改善提案
  • コードの品質に関する振り返り
  • 得られた知見の共有

そして、 .clinerules を書き換えさせる。 このとき、私は内容を見ながら適切と思われる補助線を引いてやり、今後に活かせる知見にたどり着くように協力する。私が直接 .clinerules をいじることは極力やってない。

フォーマットは YAML

ここはまだなかなか同様のことをしている人がいないと思ったので章立てして書いておく。振り返りでの出来事

ms2sato「人間に読めなくてもいい、 Roo が間違えにくく、素早く読める形式を提案して」
Roo「mdよりもyaml の方がいい」
(一度yamlにしてもらう)
ms2sato 「この変更で、前のmd版と同じ判断ができますか?」
(足りてないと言われるので、改善させて何度も聞く)
Roo「yamlだとだめ。mdの方がいい」(まさか根を上げるとは笑)
ms2sato「もしかして、 yaml 内にメタデータやルールを書くようにしたら良い?
Roo「やってみる。こっちの方がいい。yamlにする!」
ms2sato(すごい!賢い!自分を改善できる!)

こんなことがあって、今は独自形式のyamlになっている。また、メタデータ的に作りたい内容を提案してくれて、そのまま利用している。英単語なので普通に人間に追える。独自のスキーマ、言語体系を作られた感じがする。彼らにとっては助詞助動詞とかわかりにくいだろうから、そういう方がいいのだろう。

これについては他の人のところでどうなるかなどぜひ教えてほしい。私はYAMLが良い気がする(自分にとってはこの発見はブレークスルーだった)。

Makdownの時代に起こった「省略」事件

.clinerules が肥大化し500行以上になったときに起こったこと。

肥大化自体は以下のような問題を引き起こす。

- (よく言われていること)読み込みが重くなり、時間、お金がかかる
    - .clinerules を読むだけで $0.4 とか orz…
- (特殊なこと).clinerulesの編集をサボり出す。`[...中略]` のような記号を使ってファイルに書き込もうとする

特に省略されると今までの情報が消えてしまうので困る。どうも具合が悪くなった。

単純に「ファイルを小さくせよ」と伝えた場合

- 前の情報のうち大事なコンテキストを削られてしまい、うまく残らない。
- かと言って「この変更で、前のmd版と同じ判断ができますか?」 と繰り返すと元の内容とほぼ変わらない。
- 精度とファイルサイズの兼ね合いが難しかった

(そして今は YAML なので 500 行以上のファイルも適切に読み書きしているし、無駄な課金も無くなったと思う)

理由が適切な判断に必要なことがあるらしい

単純にうまくできないことをチェックリスト等にさせてみたりしたが、どうにもうまくやってくれない時期があり、改善のための対話を続けていくと「チェックリストが必要な意味がわからない」というようなことを言われた。なので、各チェックリストを適切にこなさないと起こる問題や背景を説明したのと「PRにそのチェックリストを貼らせて人間がチェックする」ということを .clinerules に書かせたらいい感じになったような気がする。YAML 内に reason のような項目があるのはこの為で、こういうのがないと、判断の方向が鈍るのかもしれない。

簡単なルールは .clinerules に追加すれば良い

例えば以下のようなことは書き込んでいつもやってくれるようにしている。

  • 「Fin」だけの文字列を私が打ち込んだらタスクが終了したと認識するようにさせている
  • IssueのURL を与えられたら gh view を使って調べるようにさせている(何もしないとブラウザを開こうとしてしまう)
  • 基本的にイテレーティブな開発や、TDD などを意識するように書いてある(テストを先に書いたりするようになった。大きいときにタスク分割の提案をするのはまだ難しいかも)

リアルな活用の体験談

以下はメモからコピペ。

  • 他のプロジェクトのコードを書き、人と会話しつつ、裏で Roo に書かせておく
    • 何かあったら ピコリン♩と音がするので見にゆく
      • 終わった時
      • コマンドの許可が必要な時
      • 何か判断が必要な時
    • 拡張ウィンドウを Roo 領域として用意
  • 土日など、ダラダラしたいときに、とりあえず何か指令だけ送っておき、ダラダラする
    • これでも何かコード書くのはいける

このブログも、裏で Roo にコードを書かせつつ、メンバーのコードレビューしたりとかしながら書いた。

対人間に近いところが多い

Roo が相手でなくても何人かメンバーを引っ張っている人なら普通なことが多い気がする。ただ、そういう感じで人間が一人増えた感じにかなり近い体験になっているのは自分として良かった。

ずいぶん昔に勉強会でこんな内容で話したりしたが、小規模チームでまだ技能が高くないメンバーと仕事しているときに感覚はとても近い。

www.slideshare.net

  • 振り返りの対話で気づいて実力が上がる
  • 理由を伝えた方がちゃんと判断できる
  • ワークフローなどは明文化する方が適切
  • Issueは小さい方がいい

などなど。ツールというより、人扱いしてしまう感じがある。

今の .clinerules

metadata:
  version: '1.0'
  schema: 'enhanced-rules-v1'
  rule_types:
    immediate_action:
      description: '即座に対応が必要なルール'
      priority: 1
      response_time: 'immediate'
    blocking:
      description: '処理を中断すべきルール'
      priority: 2
    warning:
      description: '警告を発するルール'
      priority: 3
    guideline:
      description: '推奨事項'
      priority: 4

workflow:
  development_cycle:
    rule_type: 'blocking'
    description: 'イテレーティブな開発サイクルを強制'
    steps:
      - order: 1
        name: '実装スコープの定義'
        requirements:
          - '機能を最小単位に分割'
          - '変更量を制限(50行以内を推奨)'
          - 'インターフェースの定義を優先'
        max_changes_per_iteration:
          lines: 50
          description: '1つの変更に含める行数を制限(追加・削除の合計)'

      - order: 2
        name: 'テストファースト'
        requirements:
          - 'テストファイルの作成・更新'
          - '失敗するテストの作成'
        validation:
          command: 'npm test'
          expected_result: 'fail'

      - order: 3
        name: '実装'
        requirements:
          - 'テストを通過する最小限の実装'
          - '型定義の追加'
        validation:
          command: 'npm test'
          expected_result: 'pass'

      - order: 4
        name: 'リファクタリング'
        requirements:
          - 'コードの整理'
          - '型の改善'
        validation:
          commands:
            - 'npm run lint:type'
            - 'npm test'
          expected_result: 'pass'

    error_handling:
      - condition: 'テスト失敗'
        action: 'block_next_step'
        message: '現在のステップのテストを修正してください'
      
      - condition: '型エラー'
        action: 'block_next_step'
        message: '型の問題を解決してください'

  basic:
    task_branch:
      rule_type: 'blocking'
      conditions:
        max_tasks_per_branch: 1
        error_message: '複数タスクの混在は禁止'
        error_action: 'block_operation'
        recovery_steps:
          - '新しいブランチを作成'
          - 'タスクを分割'

    quality_checks:
      rule_type: 'blocking'
      required_commands:
        - command: 'npm test'
          description: 'テスト実行'

    pr_preparation:
      rule_type: 'blocking'
      description: 'PR作成前に最新mainを取得'
      commands:
        - 'git fetch origin'
        - 'git checkout main && git pull'

    task_completion:
      review:
        rule_type: 'blocking'
        description: 'タスク完了時に必ず振り返りを実施'
        points:
          - type: 'time_analysis'
            description: '時間がかかった箇所の特定と改善提案'
            required: true
          - type: 'code_quality'
            description: 'コードの品質に関する振り返り'
            required: true
          - type: 'knowledge_sharing'
            description: '得られた知見の共有'
            required: true
          - type: 'improvement_process'
            description: '改善の実施プロセス'
            required: true
            steps:
              - 'チャットで提案'
              - '許可を得てから実施'
      
      clinerules_updates:
        rule_type: 'guideline'
        rules:
          - '情報は1箇所にまとめる(他ファイルへの参照を避ける)'
          - '設定ファイルの内容も必要に応じて記載'
          - 'トークン消費と作業効率を考慮する'

  task_states:
    completion:
      rule_type: 'guideline'
      keywords:
        - 'Fin'
        - 'End'
        - 'Close'
        - 'Closed'
        - 'Merged'

  checklists:
    start:
      rule_type: 'blocking'
      description: '作業を開始する前に、以下の項目を確認すること'
      items:
        - description: 'Issue URLが提供された場合、確認'
          command: 'gh issue view <number>'
          required: true
        - description: '最新のmainを取得'
          commands:
            - 'git fetch origin'
            - 'git checkout main && git pull'
          required: true
        - description: '作業用ブランチの作成'
          command: 'git checkout -b <branch-name>'
          required: true
        - description: '修正範囲の特定'
          required: true
        - description: 'テスト方針の確認'
          required: true

    pre_commit:
      rule_type: 'blocking'
      description: 'コミットする前に、以下のコマンドを必ず実行すること'
      commands:
        - command: 'npm test'
          description: 'テストを実行して機能の正常性を確認'
          required: true

    pre_push:
      rule_type: 'blocking'
      items:
        - 'コミット前チェックリストが完了していること'
        - 'git statusでプッシュ対象の変更を確認'

    rules:
      rule_type: 'blocking'
      items:
        - '全てのチェック項目を必ず実行する'
        - 'チェック失敗時は即座に修正する'

  branch_naming:
    rule_type: 'blocking'
    prefixes:
      feature: '新機能開発'
      fix: 'バグ修正'
      docs: 'ドキュメント関連'
      refactor: 'リファクタリング'
      test: 'テスト関連'
    validation:
      pattern: '^(feature|fix|docs|refactor|test)/'
      error_message: '指定されたプレフィックスを使用してください'

  commit_messages:
    rule_type: 'blocking'
    prefixes:
      feat: '新機能'
      fix: 'バグ修正'
      docs: 'ドキュメント関連([skip ci] タグ必須)'
      style: 'コードスタイルの修正(ロジックの変更を伴わない)'
      refactor: 'リファクタリング'
      test: 'テスト関連'
      chore: 'ビルドプロセスやツール関連'
    validation:
      pattern: '^(feat|fix|docs|style|refactor|test|chore): .+(\[skip ci\])?$'
      error_message: '正しいプレフィックスと形式を使用してください。docs の場合は [skip ci] タグが必須です。'
    docs_requirements:
      skip_ci:
        required: true
        description: 'ドキュメント関連の変更には [skip ci] タグが必須'
        example: 'docs: update readme [skip ci]'

  pull_requests:
    creation_steps:
      rule_type: 'blocking'
      steps:
        - order: 1
          name: 'コミット前チェックリストの完了'
          reason: 'コードの品質を確保し、CIが成功することを確認'
          prerequisites: []
          error_conditions:
            - condition: 'チェックリスト未完了'
              certainty: 1.0
              error_message: 'CIが失敗する可能性'
              required_action: 'complete_checklist'

        - order: 2
          name: '変更のプッシュ'
          reason: 'PRの作成にはリモートブランチが必要'
          prerequisites: ['コミット前チェックリスト完了']
          error_conditions:
            - condition: 'プッシュ前のPR作成'
              certainty: 1.0
              error_message: 'Head sha can\'t be blank エラーで失敗'
              required_action: 'push_changes'

        - order: 3
          name: 'PR作成'
          reason: 'レビュー依頼のため'
          prerequisites: ['コミット前チェックリスト完了', '変更のプッシュ']
          error_conditions:
            - condition: 'プッシュ前のPR作成'
              certainty: 1.0
              error_message: '必ず失敗する'
              required_action: 'follow_correct_order'

    template:
      source: '.github/pull_request_template.md'
      required_sections:
        - name: 'やったこと'
          format: '箇条書きで端的にポイントを記載'
        - name: 'レビュー時に気をつけて欲しいこと、参考情報'
        - name: '疑問点、確認などあれば'
        - name: '残項目'
        - name: 'チェックリスト完了確認'
          template: |
            ## チェックリスト完了確認

            ### 作業開始時のチェックリスト
            - [ ] Issue URLが提供された場合、'gh issue view <number>' で内容を確認
            - [ ] 作業用ブランチの作成
            - [ ] 修正範囲の特定
            - [ ] テスト方針の確認

            ### コミット前のチェックリスト
            - [ ] npm test

            ### push前のチェックリスト
            - [ ] コミット前チェックリストが完了していること
            - [ ] git statusでプッシュ対象の変更を確認

            ### PR作成前のチェックリスト
            - [ ] コミット前チェックリストの完了確認
            - [ ] コミットメッセージの規約確認
            - [ ] PR作成規約の確認

    cline_requirements:
      rule_type: 'blocking'
      title:
        prefix: '[Cline]'
        required: true
      sections:
        - name: 'プロンプト履歴'
          required: true
          items:
            - 'ユーザーが指示したプロンプトを時系列で記載'
            - 'ユーザーからのフィードバックも含める'
        - name: 'コンテキスト情報'
          required: true
          items:
            - 'PRの目的と背景'
            - '関連する技術的な詳細'
            - 'プロジェクト固有の重要な機能への影響'

  issues:
    cline_requirements:
      rule_type: 'blocking'
      title:
        prefix: '[Cline]'
        required: true

coding:
  implementation_scope:
    rule_type: 'blocking'
    rules:
      max_changes_per_commit:
        lines: 50
        error_message: '変更が大きすぎます。より小さな単位に分割してください'
        description: |
          1つのコミットに含める変更量を制限します。
          - 追加・削除を合わせて50行以内を推奨
          - コメントや空行も含めてカウント
          - 自動生成されるコードは除外

      interface_first:
        rule_type: 'blocking'
        steps:
          - '型定義の作成'
          - 'インターフェースの実装'
          - '具体的な実装'

      type_safety:
        rule_type: 'immediate_action'
        validation:
          timing: 'on_save'
          command: 'npm run lint:type'
        error_handling:
          action: 'block_commit'
          message: '型エラーを修正してください'

  library_design:
    suspense:
      rule_type: 'guideline'
      principles:
        - '最小限の機能セットと単一責任の原則を遵守'
        - 'バックエンド非依存の汎用実装'
        - 'エラーハンドリングは外部実装を推奨(ErrorBoundaryパターン)'
        - 'インターフェースの後方互換性を維持'

  style:
    rule_type: 'blocking'
    rules:
      indent:
        size: 2
        type: 'space'
      quotes: 'single'
      components:
        type: 'function'
        hooks:
          prefix: 'use'
          required: true
      state_management:
        - 'Context API'
        - 'カスタムフック'

  testing:
    workflow:
      rule_type: 'blocking'
      cycle:
        - step: 'red'
          description: '失敗するテストを書く'
          validation:
            command: 'npm test'
            expected_result: 'fail'
        
        - step: 'green'
          description: 'テストを通す最小限の実装'
          validation:
            command: 'npm test'
            expected_result: 'pass'
        
        - step: 'refactor'
          description: 'コードの改善'
          validation:
            commands:
              - 'npm run lint:type'
              - 'npm test'
            expected_result: 'pass'

      requirements:
        - 'テストファーストで開発を進める'
        - '各ステップでテストを実行'
        - 'テストが失敗している状態で次のステップに進まない'

    file_naming:
      rule_type: 'blocking'
      patterns:
        - '.test.ts'
        - '.test.tsx'
    frameworks:
      unit: 'Jest'
      ui: 'React Testing Library'
    quality:
      rule_type: 'guideline'
      requirements:
        - 'ヘルパー関数による共通処理の再利用'
        - 'テストケースの目的別グループ化'
        - '重要ロジックのカバレッジ確保'
        - '異常系テストの実装'

  file_structure:
    source:
      rule_type: 'guideline'
      src:
        components: '再利用可能なコンポーネント'
        pages: 'ページコンポーネント'
        lib: 'ユーティリティ関数'
        model: 'ビジネスロジック'
        shared: '共有の型定義など'

    excluded:
      rule_type: 'blocking'
      patterns:
        - pattern: 'node_modules/'
          description: '依存関係モジュール'
        - pattern: 'dist/, build/'
          description: 'ビルド成果物'
        - pattern: 'functions/lib/'
          description: 'コンパイル済みファイル'
        - pattern: 'coverage/'
          description: 'テストカバレッジレポート'
        - pattern: '.firebase/'
          description: 'デプロイキャッシュ'

troubleshooting:
  lint:
    rule_type: 'blocking'
    process:
      - step: 1
        action: 'npm run lint:fix'
        description: '自動修正を試みる'
        certainty: 1.0
        error_prevention:
          - 'eslint設定ファイルでルールを確認'
          - '自動修正を最初に試行'
      - step: 2
        action: '手動修正'
        condition: '自動修正で解決しない場合'

    auto_fixes:
      import_order:
        command: 'npm run lint:fix'
        description: 'インポート順序の自動修正'
        config_location: 'eslint.config.js'
      code_format:
        command: 'npm run format:fix'
        description: 'コードフォーマットの自動修正'

    refactoring:
      rule_type: 'guideline'
      strategies:
        - name: 'incremental'
          description: '1ファイル1コミットで変更'
          benefits:
            - '問題の早期発見'
            - 'レビューの容易さ'
        - name: 'custom_hooks'
          description: 'ロジックの集約'
          benefits:
            - '重複の削減'
            - 'メンテナンス性向上'
            - 'テストの容易さ'

  git:
    pr_command:
      rule_type: 'blocking'
      tool: 'gh'
      required_args:
        repo: 'CircleAround/myrepo'
        head: 'プッシュ先ブランチ'
        base: 'main'
        title: '[Cline] タイトル'
        body: 'PR説明文'

    commit_management:
      rule_type: 'blocking'
      rules:
        - 'コミット番号は必ずタスク完了時に報告'
        - 'PR作成後は実際のリポジトリの状態を確認'

    shell_commands:
      rule_type: 'warning'
      quote_style:
        correct:
          style: "シングルクォート(')"
          example: "'gh issue view <number>'"
        incorrect:
          style: "バッククォート(`)"
          example: "`gh issue view <number>`"

security:
  clineignore:
    rule_type: 'immediate_action'
    excluded_categories:
      - name: '環境変数とシークレット'
        examples: ['.env関連ファイル', '証明書']
      - name: '依存関係'
        examples: ['node_modules']
      - name: 'ビルドとコンパイル済みファイル'
      - name: 'システムファイル'
      - name: 'ログファイル'
      - name: 'キャッシュファイル'

    update_requirements:
      rule_type: 'blocking'
      rules:
        - '変更の理由と影響範囲を明確にすること'
        - 'セキュリティリスクの有無を評価すること'
        - '変更後にテストを実行して影響がないことを確認すること'

  sensitive_information:
    rule_type: 'immediate_action'
    restricted_files:
      - pattern: '.env'
        action: 'immediate_stop'
        message: '即座に利用を停止'
      - pattern: 'APIキー、トークン、認証情報を含むすべてのファイル'
        action: 'immediate_stop'
        message: '読み取り・変更を禁止'

    rules:
      - rule: 'APIキーなどの機密情報はコミットしない'
        detection:
          patterns: ['api_key', 'token', 'secret']
          locations: ['diff', 'file_content']
        actions:
          - 'stop_commit'
          - 'warn_user'
          - 'suggest_env_usage'
      - rule: '環境変数は .env ファイルで管理'
      - rule: 'ユーザー所有のFirestoreへのアクセスは適切な認証を必須とする'

rule_interpretation:
  context_rules:
    - name: 'priority_override'
      description: '優先度の高いルールが競合した場合、より高い優先度のルールを適用'
    - name: 'immediate_action_first'
      description: 'immediate_action タイプのルールは他のルールより優先'
    - name: 'blocking_chain'
      description: 'blocking ルールが発動した場合、後続のアクションをすべて中止'
    - name: 'error_certainty'
      description: 'certainty: 1.0 の場合、必ず失敗として扱う'

なんとなく体調がおかしい時の対応

はじめに

年齢からくるものか、生活習慣からくるものかちょっとわからないですが、朝起きた時に本調子でないことがあります。頭や体が重かったりして、心と体が整っていない感じです。

そういう時に対応していることをメモしておきます。

睡眠不足なら寝る

とても簡単な理由の一つが睡眠不足だと思うのですが、これは大変簡単で「寝れば良い」一択で考えています。無理をせずに、寝ます。フルフレックスの弊社ならメンバーも不調なら寝てしまって調子良い時に仕事して欲しいです。

ただ寝るのが上手な人と下手な人がいると思うのですが、寝やすい環境づくりは必要なことがあります。

  • 食事をして満腹中枢を刺激する
  • 風呂に入って一度体を温める(その後少し涼しい部屋にいると眠気が襲ってきます。確か体がそういう仕組みなんだそうな)
  • 部屋の温度や湿度を快適にする

ちなみに予防としては「そもそも寝不足にならないようにする」なのですが話題が逸れるので今日は書きません。

漢方薬を飲む

以前メンバーに相談してみたらご家族が漢方に詳しいそうで聞いてみたところ、いくつか紹介いただきました。私は以下のあたりが合っている感じかなと思っています。もっと強力?っぽいものもあったようなんですが、そこまでじゃない感じでした。

  • 補中益気湯
  • 加味逍遙散

食事の前に服用して、食事後少しすると良い感じになることが多いです。

軽く体を動かす

これは休むのが難しい時や軽度の時には有効なのですが、単純に血流を上げてやるだけで調子上がることあるので、スクワットしたりするといきなり元気になったりします。大体コーヒー入れながらスクワットして待っているとなんか良い感じになります。

風呂に入る

寝ようと思って風呂に入ったら、逆にスッキリして問題が解消することがあります。やはり血流大事な気がします。

おしまいに

体調管理の引き出しを色々と持っていると、ダメな時に片っ端から試していけるので良いのでは?という感覚があります。どれやってもダメな時は結構シンドイんですけど。

ステップアップJavaScriptという中級になりたい人向けの書籍について。もしくは人生で初めて本を書いてみた話。

はじめに

2022年1月14日に「ステップアップJavaScript」という書籍が発売になりました。JavaScriptを学んでいく上で一度は困りそうなトピックの解説を入れつつ、動く仕組みを作りながら学ぶ本です。 是非チェックして欲しいです。

書籍の中身については弊社でも作成した書籍Webページに仕込みました。「はじめに」「目次」「実際に書籍の中で仕上げるデモ」を確認できます。

books.circlearound.co.jp

きっかけ

お問い合わせフォームから翔泳社さまに直接ご連絡いただきまして、当初は「先方が持っている企画内容でJavaScriptの書籍を書かないか?」というお話でした。大変ありがたいことであったものの「我々はその内容では書く気になれん」とワガママを言わせていただきました。私はこういうところ頑固なので、たとえ仕事をロストしても考え方を曲げたくないという面倒なところがあります。

現代のプログラミング学習において「全てを網羅するような辞書的な書籍」というものはあまり価値が高くないと考えています。それは全てインターネットにありますし、特にJavaScriptについて言えばMDNを見れば最新の正確な内容を確認することができます。

developer.mozilla.org

これをお伝えしたところで会話終了と思いきや

「それならどんな本なら書きたいんですか?」

と逆に問いかけられたので私たちで企画を練ることになり、一回目のオンラインMTGはお開きになりました。

私たちの考える中級とは?

宿題になったので、脳内にあるものを何かしらの形でアウトプットしてみる必要がありました。私と小笠原の二人で進める上でもゴールが明瞭でなければチグハグな内容になりますし、出版社側とも企画意図をしっかりと合わせたいと考えた為です。

小笠原との次のMTGの直前に私がMiroで描き殴ったのが以下の絵です。イメージとしては「私の前に面接で『中級』を名乗る人がいたらどういうスキルを持っている人を想像するだろうか」という切り口でした。

JavaScriptの中級者について考えてみた
JavaScriptの中級者について考えてみた

当初は水色と紫で色を入れた範囲を全部網羅したいと考えていました。SPAやオブジェクト指向のようなところも軽く網羅する予定で、かなり大きく出たものだと今では思います。これをまとめる中で「JavaScriptができる」と言っても実は複数の切り口がありそうだということに気づきました。本書の読み方でも示しましたが

  1. DOMの扱いやHTML5の呼び出しなどを学習する「ブラウザを使いこなす軸」
  2. JavaScript言語の特色や変遷を把握して言語を適切に使う「JavaScript言語の軸」
  3. クラスや関数を使いこなし、コードの堅牢性や再利用性を追求する「言語によらないコード感覚の軸」

というようなものです。実はさらにもう一つあり「仕事としてコードを書く際に必要な心構えや考え方」のような、弊社のトレーニングで伝えている"実際の開発現場でやれると良い振る舞いや考え方"についても強く打ち出したい、弊社がやるならそういう色を付けたいとも考えていました。書籍というものをよくわかっていない私としては、一旦これを共有してみてどういうリアクションが貰えるかから考えようとしていました。

書籍に求められる条件について

次の会合では、たたき台を提示したこともあってより具体的なことを意見交換できたのはとても良かったです。先方はより責任を持った方も含めた3名の方で対応してくださり「書籍を売る側はどのようなことを考えているのか」が私の脳内に写像できたことは、振り返ってとても重要だったと思っています。この会合で私が脳に刻んだの主に以下の3つです。

  • 長く読んでいただけるものになる方がありがたい(つまり流行りのライブラリを説明するだけの内容だと、すぐに時代遅れになってしまう)
  • あまりに上級な内容を入れようとすると買ってくれる人がかなり限定されるので、結果として売れなくなってしまうことがよくある
  • 私たち現場開発者、トレーナーが思う「中級」と書籍で言う「中級」には多少乖離がありそう(これは中級者として完成した人を指すのか、初級から中級になろうとするのかの差異かもしれません)

この辺りをポイントにできたので「何をやるべきか」がより明瞭になったと感じています。経験不足のため、当初自分が提示した内容が本としてどの程度のボリュームになるのかがイメージできていませんでした。今回の書籍を実際に手に取ると287ページとかなり分厚く、これ以上の内容を盛り込むのはどう考えても無理だったろうと感じています。上手に誘導していただいたのでしょう。

最終的に、当初の大柄すぎる内容よりも言語そのものの理解にフォーカスする方が結果が出せそうだと納得することができました。ただし、大抵の読者がブラウザ上のJavaScriptを利用している事実や、「世間では意外にDOMを理解していない人は多く、この内容は落とさない方が良い」という提示をいただき、当初は落としても良いかと考えた基本的なDOM操作の内容をおさらいする内容を入れています。

思い返して印象に残っていること

非同期処理での苦しみについて

本書は殆どの部分が「トレーニングを通じで口頭やホワイトボードなどで伝えたことのある」内容をキッチリ整えたものになっています。従って、新たに捻って生み出すよりも、過去の脳内から整理して出力し直す作業が多かったので、あまり止まらずに書き続けられました。

しかし本書で最も大事な内容の一つである非同期処理は、正直苦しみました。当初、歴史も含んだ形でアウトプットして失敗しました(これは結果的に付録になりました)。改めて歴史がどうのとかは置いておき「まず何を学べば理解が捗るか」を再考して書き直したものになっています。島田達朗さんが書いてくださった下記のレビューでも、非同期の概念のページを引用してくださり、本書の中でも印象に残る良い内容が書けたのだと思います。

note.com

async/await は大変直感的な記述で非同期を記述できる為、まずこれで「背景の深い理解は一度置いておいても、思った処理を書くことはできる」という状態にたどり着けます。その後でPromiseと対比することで理解を深めていってもらう流れにしました。歴史とは逆ですが、この方が「概念化した後、詳細を学ぶ」という順序で整理ができると考えたのです。

複数の非同期処理を順番に実行することはasync/awaitを活用できるなら書きやすいです。多くの方が次に悩むことの一つとして「同時に複数の非同期処理をスタートし、全て終わってから次の処理を行う」ということがありました。これについてはPromise.allを紹介することでフォローしています。個人的にはどちらかと言うとPromise.allの具体例をもっと書きたくなってしまうところでしたが、書籍の目標としてはasync/awaitで行う連続的な実行とPromiseの基本理解の方へフォーカスしています。

参照について2つの内容をミックスして誕生した流れ

実は当初はプリミティブ型とオブジェクト型の解説のみで一度区切っており、付録の中でメモリをイメージする内容を入れていました。一度書き上げてから見直した時に、参照の印象が薄く、書籍の目的に対して足りない気がしたことと、レビュアーの意見に「付録の方の内容が良かった」というものがあったりするなどして、付録から一気に移動してきた経緯があります。ここは結構な大工事をギリギリでしてしまいました。今から書くならもう少し表現を変えても良い気がしています。

この件の反省としては「販促の時にどういうポイントを打ち出すか」は最初に決めておくべきだったということでしょう。

それと、この話題は厳密性を上げようとすると無駄に難解になる上、処理系依存の話も多くなってしまうと思いますし、抽象化し過ぎると解像度が低すぎて伝えたいことが伝わらないはずなので「どういう脳内イメージを構築しておけばコードを書く上で勘違いしないか」に苦慮したところでもあります。あまりこれに取り組んでいる文章も見たことが無いので、意図が適切に伝わるか多少の不安もある中で書いた内容です。最終的には一定以上の内容にできたはずだと思っています。

本当はメモリの話から広げてパフォーマンスの話へ繋げたい意図もあったのですが、紙面の都合上果たせませんでした。「Array.join使う方がforで連結し続けるより速いよ、それはメモリの確保が...」みたいなことを入れたかったなぁ。

コンテンツ化に力を入れ始めた理由

もともと私はあまりコンテンツ重視の考え方をしていない方です。どちらかと言うと「困った時に適切なインターネット上の文章に到達できる」ことを重視しています。ですが、この数年の個人向けトレーニングでは、その方法では苦戦するシーンが結構出てきてしまいました。あまり正確性の高くない情報へのヒット率が高まってしまい「嘘とまではいかないけれども、適切ではない」とか「様々なシーンで利用される言葉が誤用されている」などの事が増えた為です。

そうなると「困ったことは検索して…」ということを学習して欲しいのに「検索でヒットした内容に従ったのに間違いばかりである」という誤った学習をしてしまいます。このことはかなり大きな課題だと考えています。

私の理想では「『何か作りたい』程度だったらインターネットの情報だけでいい感じにできるから、人に教わる必要などない」となって欲しいのです。そして「仕事にするくらいのレベルに達したかったり、熟練のスピードを上げたければ人に教われば良い」という状態が理想だと考えています。それにはある程度適切な情報に辿り着きやすい環境が必要です。弊社では Tech libというWebサイトを作成して、Youtubeチャンネルで話した「私たちが大はずしはしていないだろう」と思っている内容を取りまとめていますが、アクセスはまだまだ伸び悩んでいます。 techlib.circlearound.co.jp

このような動画も知識のコンテンツ化の方法としては優れていますが、書籍は出版社が私よりも多くの方へ届けてくれるので、宣伝があまりうまくない私にとってはとても良い手段の一つではないかと考えるようになりました。今後も色々と世に送り出せたら素晴らしいです。

おしまいに

あまりまとまらない話を書き殴ってしまいましたが、実績解除の記録として置いておくのも良いかと考えています。 もしステップアップJavaScriptに興味を持っていただけたら下記のWebページから是非内容を確認されて、マッチしそうなら是非お買い上げください。

books.circlearound.co.jp

f:id:ms2sato:20220204223138p:plain

オフィス引っ越しまでのあれやこれや

はじめに

公的には9/7付けでオフィスを引っ越したのだが、この情勢でしかもまともなオフィス引っ越しとしては初めてだったのでメモっておく。ちなみに次のオフィスは私の自宅と一緒になってSOHO的になるので、かなりの縮小方向。これを書いている9/9は残りの荷物が全て運び出されている日で、その作業を横目で見ながらこの文章を書いている。

前提

  • コロナ禍なので、皆で集まる作業はやらず、基本的に私が一人で進行
  • オフィス自体は5人くらいで利用のものなのでそれ程大規模ではない
  • 既に全員完全テレワーク達成しているので普段オフィスに来る人はいない
  • 私の自宅の引っ越しとオフィスの引っ越しは同時進行

一人でやると結構ハードだったというのが率直な感想。

物件探し

可能なら当時住んでいた物件に登記事務所だけ移そうと思ったのだが、それはオーナー都合で難しいということになり、新しい居場所を探すところからスタート。メンバーはフルリモートで今後はどこに住んでも問題なくなりそうだけれど、私自身は営業の都合や金融機関等周辺の事情も考えると新宿区からは離れない方が良さそうだと判断*1

  • 新宿区orその周辺程度の場所
  • 会社で契約でき、事務所の登記が可能
  • 仕事部屋or仕事スペースが生活空間以外に持てる物件であること

バーチャルオフィス等は金融機関の融資の都合上難しく、利用しないことに決めた。家の近くに超割安な物件を借りる手もあったが、そうなると大抵狭すぎるのとあまり綺麗でないということ、遊んでいる空間を作るだけになるのは浪費感があるのでやめた*2

登記可能物件はそもそも数が少ないので、少ない中で良さそうなのを見ていく形になった。幸運にも最初の頃に「最悪ここでもいい(ちょっと高いけど)」的な物件がヒットしていたので、安心して進めることができた。結果として想定の間取りや土地を実現できたと思う。大家さんも一緒に入っている物件でとても良い感じの老夫婦で楽しく暮らせそう(社交辞令かわからないけど大家さんは「コロナ明けてきたらお酒飲もうよ」と何度も誘ってくれている。誘惑に負けるかもしれない。お酒やめてるのに)。

移行プロセス

概ね以下のように進行。2つの引っ越しを同時にハンドルする形。

  1. 旧オフィス解約の手続き(2ヶ月前解約)
  2. 旧自宅解約の手続き
  3. 旧オフィス、旧自宅内の物品の整理や破棄
  4. 佐藤個人の新オフィスへの引っ越し
  5. 旧オフィスから必要なものを新オフィスへの送付
  6. 法務局へ変更登記
  7. 旧オフィスの廃棄する物品の撤去
  8. クレカの住所変更とかとにかくいろんなところの住所を片っ端から変える

代表取締役の住所の変更は引っ越しの日から2週間以内に変更登記するものなので、法務局への申請の手間を一回にすべく、個人の引っ越しとオフィスの引っ越しの日程を調整した。最終的にはオフィスの移転日をうまく調整すれば実現できたので大きな問題はなかったが、5〜7が同じ週に一度に発生したので最後の週が想定よりもアクセクしてしまった。

途中で「日中はオフィスの荷物の整理」「帰ったら自宅の荷物の整理」という期間があって、この期間は結構しんどかった。とは言え、近くオフィスを構える予定の人に必要なオフィス用品を譲渡できたのは良かったと思う。こちらの荷物も減るし、相手も無料でホワイトボードやモニター等が手に入ってWin-Win。

おしまいに

荷物の撤去も全て済み物理的なところは終わったので、手続き的なところを進めて全てが終了する。早く解放されたい。変更登記後14日経って履歴事項全部証明書が取れるようになれば、税理士の先生、社労士の先生の仕事も進み、金融機関への住所変更もできる。

大変ではあったけれども、物理オフィスを縮小するのは下記のエントリのように会社として必要なステップだったと考えているので、実現できて良かった。 circlearound.co.jp

全体を俯瞰してみると営業に繋がるパスの減少は否めないので、継続的なアクションとしてチャネルを増やしていくのはやらないといけないなと思っているところ。元々オフライン中心であったところを少しずつ変えようとしているので焦らずに積み重ねていきたい。

そんな未来予測ではありますが、これまでの繋がりから受託のお仕事もトレーニングのお仕事もいただいており、有り難い限りであります。声かけいただいたのに、手がいっぱいになっていることが多い*3ことも課題なので、この辺りも今後は解消していきたいです(きっと相性のいい業務委託の方などいらっしゃると良いんでしょうね)。

その他

これまで私の現場介入を減らすアクションを取ってきた結果(現在ほぼゼロになっている)、引っ越し期間も特に顧客プロジェクトへの影響なく進めることができ、各メンバーの成長には助けられた。今後は人数が増えていく傾向にあるのでこの状態を維持して進められると良いのだろう。自分としては営業や新メンバーの勧誘や育成、書籍を書くなどのコンテンツへ時間を充てて、IT技術者としては「困りごとの相談相手」「何かあった時に使いやすいバッファ」「生き字引き」のポジションになると思われる。

以下はモノが全部無くなってスッキリした旧オフィス。

f:id:ms2sato:20210910181115j:plain
旧オフィス1
f:id:ms2sato:20210910181152j:plain
旧オフィス2

*1:起業前からリモートワーカーだった私はフルリモートの構想はずいぶん昔から持っていて、その当時から自分が都心から離れるのは余程会社のステップが上がらないと難しいだろうと考えていた

*2:倉庫的な活用も無しではないが、倉庫に置きたいようなものがそもそも無かった

*3:新規の作り切りよりも、長期的なものを対応していくことが多いのでこの傾向強いんです