nazolabo

フリーランスのWebエンジニアが近況や思ったことを発信しています。

職業Webエンジニアにおける「実績」と「やりきる力」

※私はWeb系のエンジニアです。以下「エンジニア」というのは「Web系のプログラマー」と読み替えて頂くのが適切な場合があります。

経歴の浅い人にキャリアについて相談された時に「まず目の前のプロジェクトをちゃんとやりきったほうがいいよ」と言うようにしています。

エンジニアにおける「やりきった状態」とは

Web系プロジェクトというのはリリースしたら終わりではなく、運用に乗ってある程度安定した状態になって初めて「十分な品質で世に送ることができた」と言えます。

運用に乗せてみると「負荷に耐えられない」「必要な機能を満たしていない」といった問題が現れることがよくあります。たいていの場合はそのまま運用を継続することはできませんので改修することになります。こうしてリリース時の機能が安定して運用できるようになった状態で「やりきった」と言えるのではないかと思います。

ただし、初期の想定を大幅に超えるデータ量やアクセス数により限界になった、というようなケースはここでは別の話になると思います。あくまで「初期の想定通りのものがリリースできているか」という点になります。

やりきっていない成果とは

やりきった状態に達していない場合、極端な話「何も手を付けていない」と「完成していたが世に出なかっただけ」の違いはわかりません。また、「完成していたが世に出なかっただけ」だとしても、それが本当に「十分な品質だったか」を判断することは本人にも不可能です。

極端な例だと、明らかに大して関わっていない巨大有名プロジェクトに短期間いただけで「あれは私が作った」というケースもありますが、さすがにそこまでいかなくても、実際まともに機能するものではなかったとか、その人が退職した後にすべて作り直されたというようなケースは多々存在します。

見えていない作業

やりきっていない成果には、「見えていない作業」が抜けている場合があります。

例えば「レビューの指摘に対応する」「周辺環境を成果に合わせて変更する」「想定以上のデータがあった場合の負荷に対応する」「実際の利用率を高い状態にする」「テストを書いて品質が高い状態でリリースする」というようなものです。

現代は雑なコードを書いてとりあえず動いていれば良いという時代ではなく、継続性の高いコードを書くことが要求されます。たとえ瞬間的なリリース直後の状態で問題なくても、その後誰も手がつけられないようなコードを書いてしまっている場合は「やりきっていない」と言って良いのではないかと思います。「リリース時の想定で動いているじゃん」と言いたいところですが、リリース時に「その後継続して機能改修する」というのを想定していない人は誰もいないのではないかと思います。

プロジェクトから逃げない

例えば巨大なプロジェクトだったり炎上しているようなものだと、つい逃げたくなりがちです。

ビジネスというのは(最終的に)お金を生むためのもので、そのためにプロダクトというのは完成してからがスタートで、それが利用されなくてはいけません。

利用される段階にまで達しなかったプロダクトというのはビジネス的には価値がないものとなります。エンジニアとして何かを得た可能性はあっても、それを途中で投げ出すというのは会社から見ると何の意味もありません。

エンジニア界隈で何らかの評価を得られるかもしれませんが、会社から評価を得られるのは「ビジネスになるプロダクトを作り上げた人」です。さらにそれを連続して達成すると、「この人なら安心して任せられる」という評価に繋がり、次の大きなポジションへと繋がります。

技術力?

この相談を受けた時に、「技術力を上げたい」という相談がセットでついてくることが多いです。漠然と「今後どうしたらいいのかわからない」という不安から来るようです。

現実的な業務におけるエンジニアの作業内容は「要件をまとめて、それを適切に実装する」ということです。もちろん場合によっては技術的に難易度が高いものも登場しますが、ほとんどの日常業務で登場することは稀です。

技術的に難易度が高いものを学ぶのが不要だというわけではなく、多くの会社でビジネスとしての比重が高いのは「日常的に比率の高い作業を確実にこなせる人材」です。まずはそれをできるようになってからでいいのではないでしょうか。

まとめ

自分のキャリアもちゃんとしていないのに他人のキャリアの相談を受ける身分なのだろうかという疑問はありつつも、最近こういうことについて何度か話したので考えをまとめてみました。

なんだかんだで「やりきることが困難な状態」というのは、周りのサポートが良くない場合というのが一番多いと思いますので、チームが一丸となって問題解決に取り組める環境だと良いのではないかと思います。チームビルディング大事。

主観が強い内容ですが、「やりきる」重要性というのはエンジニアとか関係なく大事なのかなーと思います。創作界隈でも「下手でもいいから世に出す」みたいなのはよく言われてる内容なので、結局そういうことなのかなと思います。

退職しました

  • from: UUUM株式会社(2015.06-2019.10)
  • to: まだないしょ

在籍期間中に会社の技術ブログに書いた記事は以下になります。

NESエミュレーターでRustの勉強をした

年明けから特にやることがなかったので、ファミコンのエミューレータを書くのは新しい言語に触れるのにちょうどいいとのことなので、RustでNESエミュレーターを書いてみました。全然動きません。Hello World!が辛うじて動くくらいです。

github.com

Rustってどんな言語?

C/C++の厳しいところを言語レベルで制約をガチガチに加えて安全にした言語、という印象です。最初、GCのあるLL感覚で学習していたのですが、それだと理解が圧倒的に厳しく、「C++のスマートポインタをデフォルトにした言語」と解釈すると圧倒的に理解しやすくなりました。

特に、以下の解説が参考になりました。

qiita.com

imoz.jp

Web系の方でも、ObjCでiOS開発しているのであれば比較的近い概念が登場するのでとっつきやすいかと思いますが、RubyPHPのような言語しか書いたことがないという人は概念が違いすぎて大変なのではないかと思います。

最初のうちはコンパイルエラーにとにかく悩まされるのですが、コンパイルエラーにヒントが出ることも多くて解決しやすく、次第にコンパイルエラーになりそうな場所がわかるようになります。一方で、ある程度書いてると「そもそも設計がダメだとビルドにたどり着けない」という現象に遭遇し、場合によっては大幅に書き直しを迫られる場面もありました。設計力も事前にある程度必要なのかと思います。

ただし型制約が厳密と言っても、Vec(配列)の範囲外アクセスなどで簡単に実行時に落ちます。このあたりはパターンマッチなどでそもそも構造的に範囲外アクセスできないようにするなどのテクニックが必要になってくるのかと思いました。

WebをRustで書くというのはあまり想像できませんが、WebAssemblyや組み込み、リアルタイム性が必要な部分などで登場する機会が増えるのではないかと思います。

C/C++を書いていたころを思い出し、書いていてとても楽しいと思いました。普通のWeb系だとGoもあるのでここまで持ち出す必要がなさそうですが、もっと本格的に書いてみたいので、どこかで仕事として書く機会があると楽しいのではないかなと思います。

NESエミュレーターって書きやすいの?

今回初めて書いたのですが、昔より日本語資料が圧倒的に多く、誰でも気軽に参入できる状態だと思います。参入しても何かいいことがあるわけではないですが…。

昔、似たような(もっと高級な)環境の上でプログラムを書いたことがあるので、アーキテクチャの理解は他の人より早いと思います。

冒頭に紹介した記事でもあるように、新しい言語を覚えるのには規模としてちょうどいいレベルではないかと思います。皆さんも是非挑戦してみてください。

ワンポイントTwelve-Factor App(12) : 管理プロセス

この記事は、The Twelve-Factor Appを補足し、実際に現代的なWebアプリケーションで適用する場合の注意点などを紹介するシリーズです。下記の原文を読んだ上でのワンポイント解説になります。

12factor.net

概要

  • 管理プロセスはアプリケーションの実行環境と同一リリースで行う
  • 単発の処理を実行できる仕組みを用意しておく

これは何を表しているか

DBのマイグレーションや単発のスクリプトを実行する場合でも、アプリケーションの実行環境と同じリリースで実行することによって、処理の実行の差を少なくすることができます。

リリースというのは「5. ビルド、リリース、実行」で解説しましたが、コードベースをビルドしたものに環境要因の設定を合成したものです。実行環境と全く同じ構成になっているので、例えば実行環境のWebサーバーを bundle exec … で起動している場合、単発スクリプトbundle exec… で実行することによって全く同じ状態で実行できるようになります。

また、REPLシェルが提供されていると、単発のスクリプトを自由に実行することが簡単になります。本番環境ではSSHなどを通じてリモートからコマンドを叩けるようになっているのが良いでしょう。

実際に運用する場合

Docker環境の場合、単発スクリプトをWebアプリケーションと全く同一のイメージ・同一の環境変数で、 docker run … で起動することによって、確実に同じリリースで実行することができます。コンテナオーケストレーションツールにはこのような単発実行の仕組みが用意されていると思いますので、それらを利用するのが良いでしょう。

本番環境へのSSHに関しては、セキュリティの懸念点や、リリース(「5. ビルド、リリース、実行」)を変更できてしまうという点、autoscaling環境ではそもそも接続先がはっきりしないという点などから、現代では可能な限り行わないほうが良いとされています。本番では実行できる単発処理を制限し、決められた単発処理だけリモートから実行できるような仕組みにしておくのが良いでしょう。

ワンポイントTwelve-Factor App(11) : ログ

この記事は、The Twelve-Factor Appを補足し、実際に現代的なWebアプリケーションで適用する場合の注意点などを紹介するシリーズです。下記の原文を読んだ上でのワンポイント解説になります。

12factor.net

概要

  • ログは全て標準出力に出す
  • (通常は)1行を1イベントとする
  • 標準出力に出したログはログルーターが適切に取り扱う

これは何を表しているか

ほとんどのWebアプリケーションフレームワークは、ログはデフォルトではファイルに出力するようになっています。しかし、「9. 廃棄容易性」といった点から、ファイルに出力してしまった場合は廃棄された場合の取り扱いが困難になってしまいます。

標準出力を唯一のログ出力先にしておくことで、そのアプリケーションがどのように動いているかに関わらず、共通でログを取り扱うことができます。標準出力に出したログはfluentdなどのログルーター(ログコレクター)で扱うことで、アプリケーションとは別にログの取り扱い方を決めることができます。特にDockerでは標準出力に出したログをDockerロギングドライバ経由で柔軟に取り扱うことができます。

実際に運用する場合

Webに限らずアプリケーションでログの取り扱いは最重要です。特に運用段階に入ると、ログのわかりやすさで障害時の対応の速さが大きく変わります。

ログをファイルに出力してしまうと、環境によって取り扱い難易度が変わってしまいます。fluendで取るという理由で一時出力先をファイルにするという場合もなくはないですが、基本的には標準出力に出しておくことでどの環境でも共通で取り扱うことができます。特にDocker環境では標準出力に出しておかないとDocker側のロギングドライバで取り扱うことができないので、必ず標準出力に出すようにしましょう。

設定で標準出力にログを出す仕組みがないログシステムでも、 /dev/stdout に送ることで標準出力に出すことが可能です。

開発環境ではターミナルにログが出ていれば通常は十分だと思いますので、標準出力をそのままターミナルに流しておきましょう。

1行1イベントとなると、CSV/TSVのような形式だとそのログの各項目が何かを判定することができなくなってしまいます。ヘッダ情報を足すにはLTSVでもいいですが、階層構造などを考えるとJSON形式で出しておくことで様々なプラットフォームでの取り扱いが簡単になります。nginxなどのログも含め、基本的にはJSON形式で出力するのが良いでしょう。

本項とは直接関係ありませんが、セキュリティの観点からパスワードなどをログに出さないようにする・問い合わせから追跡しやすいようにユーザーIDやエラーのスタックトーレスをログに出しておく、などの細かい点を抑えておくと良いです。

ワンポイントTwelve-Factor App(10) : 開発/本番一致

この記事は、The Twelve-Factor Appを補足し、実際に現代的なWebアプリケーションで適用する場合の注意点などを紹介するシリーズです。下記の原文を読んだ上でのワンポイント解説になります。

12factor.net

概要

  • 開発環境と本番環境の差異を最小限にする
  • 似たようなバックエンドを吸収するアダプタのようなものは使わない・使ってもバックエンドは開発と本番で同種にする

これは何を表しているか

「手元では動いたが本番で動かない」というのは開発あるあるです。これを防ぐには、開発環境と本番環境がほぼ同一になっているのがベストです。

環境の差異というのは、大きく「コードそのものの差異」と「周辺環境の差異」の2つに分かれます。

「コードそのものの差異」というのは、コードベースが本番と開発で大きく離れてしまうということです。リリースのできない大きな開発が続くとこのような状態になります。小さなコード量を短時間で少しずつリリースし、なるべく本番と開発のコードの量の差を減らすことが重要です。リリースの単位が大きくなると、影響範囲も大きくなり、障害の検知の難易度が上がります。細かくリリースすることで細かなフィードバックを得ることができます。

「周辺環境の差異」は、例えば「キャッシュストレージに開発ではメモリを使うが本番ではRedisを使う」「ファイルストレージに開発ではローカルファイルシステムを使うが本番ではAWS S3を使う」といったことが考えられます。ActiveRecordのようなものを使うと、DBすら「開発ではSQLite、本番ではMySQL」のような構成が可能になります(現実にはかなり厳しいですが)。しかし、これらは似ているようで違うものなので、詳細な挙動の差異により「本番だけ動かない」といったケースが発生する恐れがあります。「似ているから」「アダプタが吸収してくれるから」といって開発と本番で違うバックエンドを使うことは避けたほうがよく、避けることにより「本番で動かない」可能性を最小限にすることができます。

「3. 設定」でも出てきた通り、環境毎に設定ファイルを作ってしまうと環境の差異が大きくなります。開発環境ではデバッグ用ツールなどで多少の差異が生まれることもありますが、極力どの環境もほぼ同じの構成にするようにしましょう。

実際に運用する場合

リリースのサイクルを早めるには、開発手法の見直しが必要です。大きな機能の開発の場合でも、例えば他に影響を与えないコードは先にリリースしてしまう・バックエンドの処理だけ先にリリースしてしまうという方法でリリースの粒度を小さくすることができます。

環境の差異に関しては、開発環境ではDockerを使うことで本番とほぼ同等のバックエンドサービスを用意することができます。S3のようなマネージドサービスでも最近はminioのような互換システムがありますので(金とセキュリティが問題なければ開発時もS3を使うのが一番良いです)、通常の開発時ではdocker-composeで必要なバックエンドサービスを一発でローカルで起動できる状態にすることは容易になったと思います。

開発環境を本番に近づけるにはDocker上(あるいはもっと厳密な仮想環境)で動かすのが良いですが、実際の開発時にアプリケーションがDocker上で動くと処理速度などの点で不利なことが多いので使わないこともあります。その場合でも、例えば非開発者がローカルで動作確認したいという場合を考慮して、 docker-compose up の1コマンドだけで確実に上がる環境を用意しておくと良いです。その環境が用意できると本番でDockerで運用するのも簡単になります。

開発者が全Docker環境を使うかどうかは好みの問題があると思いますが、私は前述の通りDockerでアプリを動かすのは速度面などで不利になることが多いので、「MySQLなどのミドルウェアのみDockerで用意し、PHPRuby、Node.jsといった環境はホスト側で直接起動する」という構成が一番良いと思っており、2バージョンで起動できる環境を用意するようにしています。またこの構成だとgit pre-commit hookなども使いやすいところが良いと思います。

Dockerは仮想化ツールとは違いますので、カーネルのバージョンなどで埋められない差異が多少存在することは頭に入れておきましょう。しかし、一般的な開発でそこが問題になることはあまりありません。

ワンポイントTwelve-Factor App(9) : 廃棄容易性

この記事は、The Twelve-Factor Appを補足し、実際に現代的なWebアプリケーションで適用する場合の注意点などを紹介するシリーズです。下記の原文を読んだ上でのワンポイント解説になります。

12factor.net

概要

  • プロセスはいつでも廃棄されて良いものとする
  • ワーカープロセスなどでは、再入可能な設計にしておく

これは何を表しているか

普通のプロセスに関しては「6. プロセス」や「8. 並行性」にかかれている内容と同様です。並行性が高ければ廃棄容易性も自動的に高いものになります。

いやゆるキューから起動する遅延実行処理のような仕組みがある場合、それらの処理が途中で死んでも再実行できるか、同じ処理が複数回走っても正常な結果になるかといったことに気をつけて設計する’必要があります。

実際に運用する場合

いわゆるautoscalingみたいなことをする場合、現在動作しているアプリケーション環境は突然死んだりします。そのためローカルストレージは突然破棄されたりします。

どうしてもローカルストレージを使わないといけないということもなくはないですが、ファイルは基本的にS3などのオブジェクトストレージに保存するようにし、ログは前述の通り標準出力からDockerのロギングドライバ等を経由し外部ストレージに保存するというようにし、ローカルストレージに依存しない環境にしましょう。NFSなどで永続ストレージをマウントする方法もありますが、取り扱いが難しいので最終手段にしておくべきです。

Webアプリケーションのユーザーからのリクエストで時間のかかる処理を書いてしまうとユーザーを待たせてしまうことになってしまうため、時間のかかる処理はキューに逃がして遅延実行させるということがよくあります。例えば決済処理を遅延処理した際に途中で突然死して再実行した場合に二重決済になるといったことは障害のよくある例です。メールが2回送信されてしまうといったこともよくあります。遅延実行時に限ったことではないですが、重要な処理は再実行が可能になっているかといったことは必ず確認しておきましょう。