「規模は皆目見当つかないがいつまでにいくらで終わるか確約せよ」にどう対応するか?

エンジニアのための見積もり実践入門を読んでいて、ふと思いついたことを書く。

開発における見積もりは、なにを作るのか明確でないと不可能である。なにがいつまでにいくらでできるのか、を予測するのが見積もりなのだから、"なにが"の部分が曖昧糢糊としていたら予測もなにもない。

しかし、現実には表題のようなことを要求されることがままある。この場合どうすべきか?

無理なものは無理

一番まずいのは安請け合いであろう。「出来ます出来ます大丈夫です」といい加減に答えておいて、運よく工数が生返事の範囲内に収まっていればOK、収まらなかったらごまかす、というわけだが、いくらなんでもこれはない。不誠実であるし、モラルの問題を脇においても、もしプロジェクトが破綻すれば――おそらく破綻する。システム開発は発注者と開発者の二人三脚である。開発者だけが孤軍奮闘したところで成功は期待できない――経済的な被害が発注者・受注者ともに大きく発生しかねない。

といって無理なものは無理である。そのため、なぜこんな無茶を言い出しているのか、言い出しっぺである発注者に聞いて深堀していかねばならないだろう。それが仕事に対する職業人として誠実な態度であると思う。単に軽い気持ちで言ってみただけであれば、発注者側も問題を起こしたいわけではないだろうから、話し合いに応じてくれるはずだ。その先に解決策があるかもしれない。

例えば、予算が先に決まっているのでその範囲内で形になるものを作りたいと思っているのであれば、予算内にちゃんと収まる規模での提案ができないか探る手がある。なにもかもがよくわからない、ということであれば、まず調査から入るべきだろう。状況によっては、スケジュールを引くのがそもそも難しいとして、準委任契約+アジャイル式で進めることも、あるいは可能かもしれない。

話し合いができないケース――「うるせえ、良いからやれよ!」――にどう対応するか?

しかし世の中すべてスムーズに動けば世話がないわけで、時には発注者が自分の立場の強さ――勘違いかもしれないが――を悪用し、リスクをすべて被るよう受注者に強要することもあろう。これをやられると、受注者側としては恐怖に負けて安請け合いしたくなるかもしれない。

しかしこの場合でも、やはり安請け合いは不味い。理由は先述の通りで、同じだ。その場しのぎをすれば後で首を絞められる――もしかすると死に至るほど絞められるかもしれない。残念ながら、こういうケースではお取引を断念することも視野に入れることになろう。

いかに見積もり作業をリードするか

不味い発注者も中にはいないわけではないが、しかし大抵の場合、どうすれば妥当と言えるのかがよくわからないので易きに流れているだけ――どこに流れ着くかわかったものではないが――と考えられる。そのため、受注者としては、発注者が不慣れそうなら提案や見積もり作業のワークフローをうまくリードできるよう動くのがよいだろう。リードするのにもコストがかかるので――やりとりが打ち合わせが増える――費用対効果も考えるべきだが、慣れた発注者としか付き合わないと決め打つのは選択肢を狭めすぎるのではないか。

理想的な発注者などそうはいない。受注側だって、"自分は理想的な受注者だ"とはなかなか言えないだろう。お互い様である。システム開発は発注者と開発者の二人三脚、うまく協力したいものである。

IT業界の様々な商売の仕方についてのメモ

モノまたはコトを売るのはIT業界でも同じ。でもIT業界ならではの様々な形態が存在する。

汎用製品の販売

最初になんらかの汎用製品を開発し、それを売って対価を貰うスタイル。料金の徴収スタイルによっても若干の違いがある。

  1. 一括で対価を貰う(パッケージビジネス)
  2. 利用した時間に応じて対価をもらう(従量課金やサブスクリプション

だがいずれも製品が完成するまでのコストが大きく、しかも完成するまで1円にもならないためハイリスク(=完成するまでのコストが大きい)である、という点は同じ。ただし大量販売できれば、複製コストが極めて安いことから大きな利益が期待できる。

ということから集客が大変重要となる。つまり営業費・宣伝広告費も相当なものになる。それがないなら、その溝を埋める何かがいる。営業費も宣伝広告費も埋め合わせる何かもないなら、莫大なコストをかけて売上ゼロでも不思議はない。

総じて、基本的にハイリスクハイリターンな重厚長大型と考えるべきスタイルである。中には集客をある程度小売店が担ってくれる電子書籍のようなものもあるが、例外と考えたほうが良い。

受注生産

エンドユーザーから実現したいことを聞き、それを実現するためのモノを提案し、そして開発するスタイル。いわゆる受託開発。昔は請負契約によるものが主流だったと聞くが、昨今ではアジャイル式の準委任契約もなくはないと聞く。

汎用製品と違って売上は固定的。ただし受注さえしてしまえば、その後で莫大な集客コストをかけなくてもキッチリ売り上げが立つと期待できる。その意味で、小資本でも比較的楽に(製品販売と比べればまだまし、という意味で)商売を回せる余地がある。

ただし、作れない・作ったけど払ってくれない・不具合に対して法外な損害賠償をされる、といったトラブルのリスクをちゃんと管理できる必要がある。IT業界の受注生産は不採算案件の多さが昔から問題となっており、このリスクをいかに管理するかが重要なポイントとなる。

技術力不足で作れないリスクは、受けられるか受けられないかの予想はそれなりにつくはずだから、まだ管理できる。というか、それが全くできないなら受注生産などしてはいけない。

管理が困難なのは、エンドユーザーの非協力と無責任である。すべてのエンドユーザーがシステム開発に対する熱意(と能力)に溢れていればどんなにか素晴らしいことだろうと思うが、残念ながら現実はそうでない。なにが必要なのか自分でもわかっていないし、自分で決めたことに責任を持とうともしない、なんて恐ろしいエンドユーザーも中にはいる。そんな相手から受注してしまうと悲劇である。異様な案件に飛びついてしまわないのはもちろんだが、不幸にも後からそんな担当者に変わってしまうこともあるので、すべての卵を一つのバスケットに入れない工夫も必要となる。

また、システム開発は不具合ゼロがあり得ない――これは裁判でも認められている当然の前提である――ことから、不具合のリスクにも対応しなければいけない。これは過大な責任を負わないよう契約内容を管理するだけでは足らず、賠償責任保険の利用も合わせて行うべきである。

総じてミドルリスク・ミドルリターンと言ったところであろうが、ただし付き合う客層による。また自分の手に余る案件を引き受けると惨事を招くため、過大なリスクテイクは禁物である。

雇われて工賃を貰う

月いくら、あるいは時間いくらで働いてお金をもらうスタイル。

  1. インソーシング(いわゆる従業員雇用)
  2. アウトソーシング先となる(いわゆるSESや派遣)

リスクが低い傾向があり、またスキルが低くとも――褒められたことではないが――お金は貰えるという点で安定してはいる。コストも通常は雇用主持ちである。

ただしその安定性はマイナス方面にも作用することは忘れるべきでない。どんなに効率よく仕事を済ませても、大きな功績を挙げたとしても、収入は変わらないか微増にとどまる。夢や希望を掴める人生とは、ごく稀にある大きなチャンスを掴んだ人生のことだが、このスタイルでそのようなチャンスを掴む機会は皆無と考えてよい。ベンチャー企業ストックオプションのような例外も稀にあるが、これとてそんな好条件はそうないし、あったとしても実現するとは限らない。

という理由により、他のスタイルの上位互換ではない。ローリスク・ローリターンくらいか。だが昨今の社会事情を考えるとこのスタイルもローリスクとはいいがたくなってきており、実のところミドルリスク・ローリターンくらいのポジションかもしれない。

広告収入

直接ユーザーから対価を徴収するのではなく、広告を表示してそこから売上を立てるスタイル。ソーシャルメディア(e.g. Youtube, ブログ, etc, etc...)やサービス(e.g. ジモティ)の運営がユーザーに提供する価値となる。

問題はマネタイズのレートがあまり高くないということ。またサービスを作って広告を出す場合、汎用製品のスタイルと同じく重厚長大のケがある。

強いて言えばハイリスクからミドルリスク・ローリターンくらいか。だが水物すぎて売上をアテにし難く、よほど魅力的なコンテンツを持っていないと収入の柱とするのは困難なように思われる。

参考

GitHub Copilot in Visual Studio Codeを試してみた

かねてから気になっていたGitHub Copilotを試してみることにした。開発効率やコード品質の向上に繋げられるなら月額10$くらい安いものだが、その実力やいかに。

サインアップ

有償サービスなのでサインアップが必要である。自分が見たときは、クレジットカード/Paypalが使えるらしかった。クレジットカードを選択し、その情報と住所・名前を入力して確認すれば登録はすぐに完了した。

VSCodeを使うので、こちらのマニュアルからVSCodeに導入した。

連携の承認が必要だったが、イントロダクションに従ってポチポチクリックするだけであっけないほど完了。

20221127161342

迷うところは特になかった。

使ってみた

公式のAboutによれば、

GitHub Copilot is an AI pair programmer that offers autocomplete-style suggestions as you code. (GitHub Copilotはオートコンプリートでやるみたいにお勧めをあなたのコードに提供するAI ペアプログラマーだよ!)

だそうである。具体的にオートコンプリートはどのような場面で起動するのか、であるが、基本はコメントであるようだ。そこでコメントを下記のように記載してEnterを押すと

# ファイルを読み込む

下記のようなコードが表示された。

# ファイルを読み込む
def read_file():
    # ファイルを開く
    with open("data.txt", "r") as f:
        # ファイルの内容を読み込む
        data = f.read()
        # ファイルを閉じる
        f.close()
        # ファイルの内容を返す
        return data

TABを押すことで確定させられる。

"おいおい兄弟、ハードコーディングは勘弁してくれよ"と思うかもしれないが(自分は思った)、これはコメントのつけ方が悪い。下記のようにコメントを記載して出し直すと

# ファイルをファイル名から読み込む

ちゃんと引数を使った関数が表示された。素晴らしい。

# ファイルをファイル名から読み込む
def read_file(file_name):
    # ファイルを開く
    with open(file_name, "r") as f:
        # ファイルの内容を読み込む
        data = f.read()
        # ファイルを閉じる
        f.close()
        # ファイルの内容を返す
        return data

欲を言えば型もちゃんと指定してほしかったが、Pythonは型指定なしで書かれることも多いので、その影響だろう。十分、ともいえる。

ちなみに選択肢があればCTRL+Enterで複数の候補を表示させることもできる。

20221127164555

関数名が重要?

関数名に影響を受けるらしく、単純な動詞のほうが候補が出やすいようだ、scrapeだけであれば下記のようにそれらしいコードが表示されるのだが

20221127164204

一方で、scrape_dataとすると候補が出なかった。

20221127164207

元々のコードのベースから確からしいコードを推測して出しているのだろうから、これは納得いく理由。今ある労働のいくつかは人がAIに合わせるようになるであろうと思っていたが、プログラミングの世界がすでにそうなっていたことを感じさせる。

具体的な、いわゆる下位問題を解決するための関数の作成には、かなりの威力を発揮しそうな雰囲気である。実に素晴らしい。

ある程度抽象的な関数にも対応してくれるのか?

理想は抽象的なビジネスモデルをブレイクダウンしてコードにまで落とし込んでくれることだが、さすがにそれは望み過ぎとして、実際どの程度であれば抽象的な問題にも対応できるのか。

というわけで下記のコード(C#)を自前で書いた上で、

class Member{
    public int Id  {get; set;}
    public string Name {get; set;}
}

class MemberServiceで利用してみたところ、下記のようになった。

class MemberService{
    public Member GetMember(int id){
        return new Member {Id = id, Name = "John"};
    }

    public void SaveMember(Member member){
        //save member
    }

    public void DeleteMember(int id){
        //delete member
    }
}

まあ悪くない。EFを使ってくれても良かったか? しかしこんなものだろう。

むしろ驚いたのはTestMemberServiceと打ってCTRL+Enterで候補を一覧表示させたときのことで、なんと下記のようなコードが表示された。

class TestMemberService{
    public void TestGetMember(){
        MemberService memberService = new MemberService();
        Member member = memberService.GetMember(1);
        Assert.AreEqual(1, member.Id);
        Assert.AreEqual("John", member.Name);
    }

    public void TestSaveMember(){
        MemberService memberService = new MemberService();
        Member member = new Member {Id = 1, Name = "John"};
        memberService.SaveMember(member);
        //assert if member is saved
    }

    public void TestDeleteMember(){
        MemberService memberService = new MemberService();
        memberService.DeleteMember(1);
        //assert if member is deleted
    }

}

なにこれ凄い。しかも驚いたことに、既存クラスのプロパティが変わると追跡した(!)。これは楽である。

実際どう?

もっと使ってから判断すべきではあるが、極めて便利なツールと感じる。少なくとも自分の中では、プログラミングを行い続ける間、Github Copilotを是非使い続けたいと思う。

AWSでなぜコンテナ・Dockerを使うのか?

Software Design (ソフトウェアデザイン) 2022年11月号で"なぜコンテナ・Dockerを使うのか?"という問いかけがなされていたので、自分も思うところを書いてみたい。

コンテナのメリットは?

自分が思うコンテナのメリットは次の3つ。

  1. 環境を再現しやすい
  2. 一つ一つのコンテナの設定が分離しているので把握しやすい
  3. AWS ECSやECRといった様々なマネージドサービスに乗っかれる

1. 環境を再現しやすい

なんといっても環境を再現しやすい。ホストに直接ミドルウェアをインストールしつつ動かす方式だと、ホストに施した設定に依存する点が多く、"〇〇だと動いたのに!"なんてことが大変多い。下手をすると、本番環境で使いたいOSだと動作しない、などということさえ出かねない。

この点、コンテナで動かすようにしていると、基本的にコンテナが動くなら環境を選ばない(※)。これはC#.NETのようにどちらかというとマイナー気味なフレームワーク(失礼)を使う際には大きな安心感となる。

※ ただし絶対ではない。例えばARM/x64のようにCPUのアーキテクチャが違うとか。

またチーム開発の場合、別のメンバー(含む六か月後の自分)に同一の開発環境を提供するのも容易い。これも嬉しいところ。

2. 一つ一つのコンテナの設定が分離するので把握しやすい

一つ一つのコンテナに必要な分をDockerfileにまとめることになるため、それぞれの記載内容が自然と限定される。これは大きなメリットで、どこになにが依存しているのかわかったものじゃない、という惨状を避けることができる。

肥大化した超巨大な設定群がどこにどう依存しているのかよくわからないままモリッとかけられていて、もうなにが必要でなにが不要なのかもわからない、今さら各モジュールごとに分離するのも至難を極める、なんて地獄の状況に陥りづらい、ということである。これはとてもとてもとても助かる。

3. AWS ECSやECRをはじめとする様々なマネージドサービスに乗っかれる

ECSにはAutoscaling(ということはAuto Recoveryも含む)やデプロイ、ロールバックといった仕組みが予め備わっている。ビルドした結果を保存するためのECRもある。これらを自前の仕組みで構築しなくて済むのは大変に助かる。

CPUやメモリをはじめとするメトリクスも元から使えるので監視のためのソリューションを入れる必要もなく、大変よろしい。

これが自分の中では最大の理由で、もしこれらの優秀なマネージドサービスがなかったらコンテナ化には二の足を踏んだことだろう。

ではコンテナ化のデメリットは?

構成が複雑化する。この結果、構築に一手間増える他、トラブルシューティングに苦労しやすい。

それに見合うだけのメリットは十分にあるのだが、ECSで構築した一連のリソース群がうまく動かなかった時はウッとなる。ネットワーキングからアプリまで、あちこちに障害の原因があり得るので切り分けが大変なのだ。

EC2じゃダメなんです?

EC2ではダメということはない。ダメということはないのだが、トラブルシューティングはどちらかというとEC2直で運用しているほうがやりやすかったりもするのだが、EC2上でプロセス監視を入れてオートリカバリを入れてディスク監視入れてメモリ監視入れてCPU監視入れてホストのセキュリティパッチあてて、とやりながらデプロイどうしようとかロールバックどうしようとかあれこれあれこれ考えて構築しないといけないのはなかなか重い作業で、正直特に理由がないならEC2直は避けたいのが本音である。これらはサービスを動かすための要因でしかなく、頑張って作ってもサービスの使い勝手がよくなるわけではない――不便になるのをある程度防ぎはする――という点も悩ましい。

オンプレで物理サーバーを自前で運用することを思えば、EC2も隔世の感がある素晴らしいサービスなのだが、ECSに慣れてしまうと、うん。

Cannotpullcontainererror: pull image manifest has been retried 5 time(s): failed to resolve ref docker.io/library/nginx:latest: failed to do request

先の記事の後にもIPv6 Only VPCを試していたのだが、思わぬところに落とし穴がやはりあった。

問題

"Cannotpullcontainererror: pull image manifest has been retried 5 time(s): failed to resolve ref docker.io/library/nginx:latest: failed to do request"

Fargateを試していたのだが、どういうわけかDockerHubからコンテナが取得できない。上述のエラーメッセージが表示される。

なんとDockerHubがIPv6非対応らしい。

解決

仕方ないのでSubnetにIPv4を設定し、IPv4/IPv6デュアルスタックにして対応したところ解決した。しかしこの対応だと、Private Subnetに置くためにはNatゲートウェイが必要になるわけで、なんともトホホな結果である。

SecurityGroupで接続元をALBに限定すればpublic subnetに置いていてもおかしな通信を叩き込まれることはないはず、と言えばそうなのだが、SGの設定を間違えると素通しになるわけで、あまり嬉しくない防御法である。

その他

ちゃんと検証していないが、こちらの記事にもある通り、Target GroupもIPv4を使っていそうな雰囲気。

あとECRがIPv6対応しているのか公式ドキュメントを探しても記載箇所が見つからない。これもかなり心配な感じである。こちらはGatewayを使えばいいのかもしれないがegress only IGWで対応できないとなるとコスト面で(業務用なら屁でもないのだが)辛みがでる。

総じてIPv6 onlyはまだまだいばらの道という感がある。IPv6自体は便利だと思うのだが、周辺サービスがついてきていない。業務用だと、当面はIPv4を大人しく使うか、デュアルスタックで部分的に導入するか、といった選択になるのではなかろうか。見切り発車でIPv6 onlyを選択すると大変苦労するであろう。

IPv6オンリーのVPCで遊んでみた

タイトルの通り、IPv6オンリーのVPCを試してみた。以前の記事IPv6オンリーは業務用だと要注意、と書いたばかりだが、個人用途で試す分にはイケイケでいける。NAT ゲートウェイがいらない(Egress Only IGWが使えるので)ため安くあがる点が個人用途だとポイントである。

ネットワーク上の主な違い

  • SubnetにIPv4を振らない。
    • VPCにはIPv4を設定する必要があった。
  • NAT Gatewayがいらない。
    • 代わりにEgress-Only Internet Gatewayを使う。固定費がかからないNATゲートウェイみたいなものである。
  • Elastic IPがいらない。
    • IPv6だから元から固定IPとして使える。
  • 全ての場面でIPv6を使うことになる。
    • ローカルのPCからSSHで接続するのもIPv6である。
    • "いかん、ここはIPv4でないと困る"みたいなことがあっても対応不能である。
      • 大抵問題ないはずだが、"動くはず"と"動いた"は違う
        • という問題があるので業務用でIPv6オンリーはなかなか怖いわけだが。

気づいたこと

遊んでいて気になったことがいくつかあった。やはりIPv6オンリーで利用すると細かいところが違ってくる。

IPアドレス直での管理は避けたほうが良い

公式リファレンス

IPv6 CIDR ブロックと VPC の関連付けを解除できます。VPC から IPv6 CIDR ブロックの関連付けを解除すると、IPv6 CIDR ブロックと VPC を後で再び関連付けた場合に同じ CIDR を受け取ることは期待できません。

とある。従って、VPC内のEC2を管理するにあたって /etc/hostsにIPを直接記載したり、プログラム内でIPを直接記載したり(あまり良い造りではないが)すると変化に弱くなる。もしIPを振り直しになった場合、直接記載した部分を全部修正しないといけなくなるだろう。面倒に感じても、DNSを使ってホスト名で管理したほうが良い。

ElasticIPがいらない

公式ドキュメントによると

インスタンスを停止しても、次のものは保持されます。
IPv6 アドレス。

だそうである。実際、停止→起動を行ってみても保持されたので、本当だと思われる。つまりElasticIPを使う必要がない。例えば、踏み台サーバーを普段止めておいて使うときだけ起動する使い方をしても、EIP料金を取られなくていいわけだ。地味に嬉しいポイントである。

一方で、常に固定IP状態とも言えるので、セキュリティには十分気を使わないといけないが。

書式がIPv4と異なる

IP直で接続する場合、IPv6は[]で囲む必要がある。そのためWEBサーバーにアクセスする場合でも、IPv4ならhttp://192.168.1.1/のように接続できるが、IPv6だとhttp://[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]/のように記載しないといけない。

20221115114157

これはSSHで接続する場合も同じで、configの記載が微妙に異なる。

Host bastion
    HostName xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:79c2


Host develop-target
    HostName xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:670b

Host develop-*
    ProxyCommand ssh bastion -W [%h]:%p <- ここである。
    #   ProxyCommand ssh bastion -W %h:%p <- IPv4ならこう書く。

Host *
    Port 22
    User ec2-user
    IdentityFile ~/.ssh/test.pem

多段SSHでPrivate SubnetにあるEC2インスタンスに接続する

Private SubnetにEC2インスタンスを立てて使う場合、踏み台を経由して接続できるようにしたほうが色々と便利なことが多い。

というわけで多段SSHの設定を書き残しておく。

結論

~/.ssh/config に、ProxyCommandを使いつつ、例えば下記のように記載する。

bastion(踏み台)がPublic SubnetにあるEC2インスタンス、targetがPrivate SubnetにあるEC2インスタンス

Host bastion
    HostName xxx.238.91.108 <- public ip

Host develop-target1
    HostName 10.0.72.5 <- private ip

Host develop-target2
    HostName 10.0.72.6

Host develop-*
    ProxyCommand ssh bastion -W %h:%p

Host *
    Port 22
    User ec2-user
    IdentityFile ~/.ssh/test.pem

同じ設定はまとめたほうが楽なのでまとめているが、まとめずにちゃんと全部記載するなら参考のページを参照。

これでssh develop-target1のようにコマンドをたたけば接続できる。

参考