タスクマネジメントのコツ
はじめに
エンジニアに限らずキャリアのステップアップに伴い、業務の影響範囲が広がっていくものと思います。
個人で成果を出していたものがチームになり、組織に変わり、会社全体となっていくと、自然とステークホルダーが増えてマルチタスクも抱え込みがちです。
弊社のエンジニアとの1on1の中でも同様の事象が発生し、「マルチタスクが増えてきてタスク管理が難しくなった。何か良い方法はないか?」という質問をいただきました。
私が三年ほど継続している方法で、この場で実例を持って紹介したいと思います。
事前に保険をかけておくと、これから紹介するやり方が正解というわけではないですし、もしもっと良いやり方があるようであれば、また記事として投稿したいと思っています。
タスク管理の目的とは
まず本編に入る前に、タスク管理をすることで「何が実現できていれば良いのでしょうか?」
1. タスクの抜け漏れをなくしたい 2. タスクの関係者を明確にしたい 3. 優先度を可視化したい
ぱっと思いつくのはこのあたりですが、一般的には「これからやるタスク」に注目しがちかなと思っています。 これからは「これまでやってきたタスク」にも視野を広げてみてください。
4. タスクのアサインメントが適切か振り返る
タスクの性質や粒度を理解する
弊社ではスクラム開発を採用していて、プロジェクトのタスク管理にJiraを活用しています。
一方で、マルチタスクのステークホルダーから依頼されるタスクのすべてがここに集約することはできません。
プロジェクトに関係のない「社内イベントの準備」や「経理申請」といったタスクを作成しstorypointを割り振ることもできますが、開発スコープの計画が崩れる上チームの生産性はよくわからなくなってしまいます。実際にプロジェクトへ貢献するタスクのみ
をJiraでは管理すべきです。
また、そのタスクが短期・中長期に終わらせられるものかどうかも重要です。 1日1回しか確認しないツールで時間単位のタスクを管理したとて、意味はありません。
本編
中長期のタスクはTODOアプリに集約しましょう
中長期のタスクは期間が不定であることが多いです。カレンダーで管理しても良いのですが、 未来日に設定してあると直前まで気づかずに炎上してしまうのと、期間が変更されるのに合わせてタスクを移動させるのは面倒です。
ちなみに、私はNotionでタスク管理しています。
正直カンバン形式であればなんでもいいです。
注意点は2点です。基本は進捗が可視化されていればOKだと思ってます。
- 優先順位の高いものを上へ
- 対応中のタスクは1つまで
短期解決のタスクはカレンダーに集約しましょう
マルチタスクになるということは、イコール他のメンバーとのMTGが増えるということです。 弊社ではGoogle Workspaceを利用しているため、MTGのほとんどはハングアウトです。
これに、「個人が実施しているタスク」を追加しましょう
予め作業時間として確保したいのであれば、ある程度未来時間に予定を入れても問題ありません。
1日1回カレンダーを見てみて、穴が空いてないか確認します。 空いているってことはぼーっとしてる時間があったってことでしょうか?そんなことはない、きっとなにかしていたはずです。
カレンダーに色ラベルをつけましょう
モザイクがかかっていますが、やたらとカラフルなカレンダーに見えますね。これはカレンダーに色ラベルをつけているからです。
色ラベルを使用してカレンダーの予定を管理する - パソコン - Google カレンダー ヘルプ
是非全てのタスクに色ラベルをつけていってみてください。そして、週次で時間の分析情報を振り返ってみましょう。
私はかけだしエンジニアリングマネージャーなので「どのプロダクトにどの程度コミットメントしているか」の割合を見ています(モザイクがかかっているのはサービスごとの時間)。 ここにかける時間が減ってこれば、手が離れても自走できているチームということだなという目安になります。
場合によってはマネジメントに切り口によってラベル付するのも良いかもしれません
- ピープルマネジメント
- テクノロジーマネジメント
- プロジェクトマネジメント
- プロダクトマネジメント
あとはエンジニアであれば
- フロントエンド開発
- バックエンド開発
- インフラ開発
とか、開発プロセスでもいいかもしれないですね。
- 設計
- コーディング
- 分析
- 企画
何の切り口で見るべきかは、「自分が意識しないといけないこと」に合わせてラベルを更新していけばよいと思います。
つけられるラベル数には限界があるのでそこも注意が必要です。
所感
カレンダーでのタスク管理は、元々エンジニアリングマネージャーにはじめてなったときに「タスクの棚卸し」が目的でした。
実際には自分の目指すキャリアにどれだけ近づけられるか?という観点を持つことができるツールとして役立てています。
また、それだけでは中長期のタスクがどうしてもカバーできない(忘れてしまうw)ので、別のToDOアプリを採用していますが、 Google Workspaceやカレンダーでそれに代替する機能が今後出てくれると嬉しいなと思います。
GitHub Actionsでコードの差分からデプロイ先を指定する
はじめに
モノレポでのプロジェクトの際に、ファイルの変更差分に合わせてビルドやデプロイ先の指定がしたいケースがあると思います。 GItHub Actionsでどのように表現するか、備忘として記録します。 turborepoでの構成を例に書いていきます。 turbo.build
リポジトリ構成
. ├── .github │ └── workflows │ └── deploy.yml ├── apps │ ├── app1 │ └── app2 ├── node_modules ├── package.json ├── packages │ └── tsconfig ├── turbo.json └── yarn.lock
deploy.ymlファイル
ファイルの変更差分はactions/checkoutパッケージを利用します github.com
jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 2 - name: Set Git Diff id: diff run: | echo "APP1_DIFF=$(git diff --name-only HEAD~ HEAD --relative ./apps/app1 | wc -l)" >> $GITHUB_OUTPUT echo "APP2_DIFF=$(git diff --name-only HEAD~ HEAD --relative ./apps/app2 | wc -l)" >> $GITHUB_OUTPUT echo "ALL_DIFF=$(git diff --name-only HEAD~ HEAD --relative ':!./apps'| wc -l)" >> $GITHUB_OUTPUT - name: ALL Deploy if: ${{ steps.diff.outputs.ALL_DIFF != '0' }} run: | [app1, app2 deploy command] - name: App1 Deploy if: ${{ steps.diff.outputs.ALL_DIFF == '0' && steps.diff.outputs.APP1_DIFF != '0' }} run: | [app1 deploy command] - name: App2 Deploy if: ${{ steps.diff.outputs.ALL_DIFF == '0' && steps.diff.outputs.APP2_DIFF != '0' }} run: | [app2 deploy command]
ポイント
fetch-depth
ファイル変更差分を取得したいので fetch-depth: 2
を指定。ここは運用やデプロイルールにも依存するところかもしれませんが、
mergeをトリガーにdeployする場合はマージコミットが取りたいので 1. HEAD
, 2. HEAD~
でdepth: 2を指定しています
GitHub - actions/checkout: Action for checking out a repo
git diffの差分結果をGITHUB_OUTPUTに出力パラメータとして設定
コマンド結果を$GITHUB_OUTPUTとして指定しておくと、以降のstepで参照する事ができます。
${{ steps.diff.outputs.xxxx }}
https://docs.github.com/ja/actions/using-workflows/workflow-commands-for-github-actions
ちなみに本件とは直接関係ないですが、::set-output
を用いた出力パラメータの記載方法もありますが、利用が非推奨となっているので、
本稿記載の方法がおすすめです
github.blog
所感
ちょっと泥臭いので、もっとスマートなやり方できないか模索中。
PdMを目指すエンジニアについて思うこと
はじめに
近年のweb市場では、DXの必要性に合わせてPdM人材が求められています。 採用でもPdMをやってきた人、これから目指したい人が増えてきている印象で、私自身もPdMを名乗っているときもあります。
一方で、エンジニア畑の人たちがPdMを目指したいというときに 「エンジニアリングから離れないといけないのではないか?」と考えていて キャリアを閉ざす人がいると思います。
これらに関する雑感を書きます。
PdMってなんだ?PjMとの違いは?
私が人に説明する際に話すのは下記です。
- PjM(プロジェクトマネージャー)はプロジェクトを終わらせるように進める管理者
- PdM(プロダクトマネージャー)はプロダクトを終わらせないように進める管理者
PdMの一般的な業務は以下だと認識しています。
- 企画立案・要件定義
- 進捗管理
- KGI・KPI設計
- ロードマップ策定
- データ分析 corp.tech.hipro-job.jp
よく「何を作るか(what)」「なぜ作るのか(why)」を追求していく人と言われますね。
ミニCEOだと言われたり、言われなかったり。
ここで重要なのが、PdMの業務には基本設計するとか、コーディングするとかいった「エンジニアリングの要素」が1ミリも入ってないというところ。
異なる畑へのステップアップに見えるため、「エンジニアリングを捨てて学ばなければならない?」と思われる方が多い印象です。
エンジニアリングの本質は課題解決
「コーディングだけやれば良いという時代は終わった」と私は考えています。
何をつくっているか、なぜつくっているかまでを知っておかないと、不要なシステムを作り込んだり、作ったものの評価を正当に受けられない可能性があります。
それは事業貢献だけでなく、自信のエンジニアスキルへのフィードバックもなくメリットは何もありません。
広義の意味で、エンジニアはPdMの領域に足を踏み込んでおかなければより良いものづくりはできないと思っています。
超曖昧なベン図ですが書いてみました。 エンジニアとPdMの間の定義というのが一般的なのかわからないですが、「企画エンジニア」とあったので追加しています。 みなさんはどの領域を目指していますか?
ちなみに私が目指している領域のベン図は下記です。エキスパート以外の領域は網羅したいと思っています。
「やること多いじゃん!」って思うかもしれませんが、はい。やることは広義ですがシンプルです。
やること * プロダクト開発 * 組織改善
やらないこと * 超難しいテックリードでないとできないような技術的挑戦 * デザイン * 一人で開発すること * 営業活動 * エンジニア以外の組織改善
組織改善の文脈を除外すれば、一般的に言う「ジェネラリスト」の領域かもしれないですね。
なぜこの領域を目指しているか
「開発現場で求められているから」です。 プロダクト開発においてQCDは非常に大切です。
このQCDという言葉は昔から使われており、WEBの成長速度は早いと言われていますが、近年だと更にDelivery(事業提供)が求められていると感じています。
開発生産性指標であるfour keysでも、サービス復旧時間が求められており、Deliveryの重要性を鑑みることができます。
https://cloud.google.com/blog/ja/products/gcp/using-the-four-keys-to-measure-your-devops-performance
最速のDeliveryとは
極端な例で、実際には定例じゃなくSlackやChatworkなどでやりとりもあるんでしょうが、コミュニケーション数としては正しい。
1次情報、2次情報と間に人が入れば入るほど伝書鳩役の人が増えて、作りたいものが間違うリスクはもちろん、スピードも遅くなります。
つまり「自分がつくれるが最強最速」である。
とはいえ
個人がPdMとエンジニア両立して学びながらは大変なため、チームとして支えて上げる必要があると思っています。
そのためにはチーム間で「その人の目指すキャリアを共有すること」が大切です。
テックリード、エンジニアリングマネージャー、PdMなどチームメンバーが目指していることに近しいタスクをアサインします。
余ったタスクは要相談です。今回のように「PdMがやるべきタスク」であればその人のために残してあげるべきですし、
「誰がやっても同じだよね」というタスクは譲歩してやってあげましょう。過多がないように。
往々にしてPdM=事業責任者=誰もやらないタスクをやる という誤解が生まれがちですが、チームみんなのタスクであることを認識しないといけません。
おわりに
今回PdMを例に上げていますが、これは別の職種でも同じ話かと思います。
私のチームではキャリアビジョンの共有・透明性を大切にしていて、プロジェクトのキックオフの際には各メンバーへ期待する役割を公開しています。
人一人が働ける時間は決まっていますが、メンバーと支え合うことでタスクを分担したり、より自分のなりたいキャリアを目指すことができるのもチームの良いところだと思ってます。
BigQueryのSQL実行結果をGoogleSpreadSheetに定期出力
はじめに
前回の記事でもご紹介しましたが、私の担当プロダクトでは「自分の開発したものは自分で効果測定しましょう」 というルールがあります。
エンジニアの場合、「BigQueryにデータあるからSQL書いて分析してね」で完了ですが、非エンジニアの場合はそうはいきません。
SQLってなに?から始まって、ER図の見方、各種テーブル・カラムの責務の説明など、なかなかに学習コストが高いのが現実です。 また、「定期的に更新してほしい」という要望もよくありますが、エンジニアでもどう実現するかは一度考えなくてはなりません。
今回紹介するのはGoogle App Scriptを利用した方法です。
メリットは、実現がめちゃくちゃ簡単でシンプルなことです。
※なんだGASかよ、と思う気持ちはわかりますがここにコストかけるほうがナンセンスです
SpreadSheetの準備
新規でスプレッドシートを生成したら、下記のようなシート構成にします。
- settings
- output_sheet_1
- 出力先1
- output_sheet_2
- 出力先2
GASの準備
APIの追加
新規でGASファイルを生成したら、サービスにBigQuery APIを追加します。
ソースコードの貼り付け
下記をソースコードに貼り付け。SHEET_IDは前述したスプレッドシートのものを指定してください。
function bigQuery() { const spreadsheet = SpreadsheetApp.openById("SHEET_ID"); const querySheet = spreadsheet.getSheetByName("settings"); querySheet.getDataRange().getValues().map((row, index) => { const [projectId, query, output] = row; if (projectId === "projectId") { return } console.log(output); const result = BigQuery.Jobs.query( { useLegacySql: false, timeoutMs: "1000000", location: "asia-northeast1", query: query, }, projectId ); const rows = result.rows.map(row => { return row.f.map(cell => cell.v) }) const resultSheet = spreadsheet.getSheetByName(output) // header を退避 > シートをリセット > header追加 const header = fetchHeader(resultSheet) resultSheet.clearContents() resultSheet.getRange(1, 1, 1, header.length).setValues([header]) resultSheet.getRange(2, 1, rows.length, rows[0].length).setValues(rows) // 実行ログ querySheet.getRange(1 + index, row.length,1,1).setValue(new Date().toLocaleString('ja-JP')) }); } function fetchHeader(sheet) { var last_col = sheet.getLastColumn(); return sheet.getRange(1, 1, 1, last_col).getValues().find(value => true); }
ポイント
- BigQuery.Jobs.queryのuseLegacySql: falseに設定する
- これをやらないとSQLが実行されないです
- BigQuery.Jobs.queryのtimeoutMs: "1000000"
- 重めのSQL実行する場合は、タイムアウトしてしまうので値を大きめにしておいたほうが良いです。
- ちなみにデフォルトは10秒です。
- https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query
- 出力先シートへの更新は下記手順で行っていて、何度実行しても破損しない形にしてあります。
- ヘッダーの退避 > シートのリセット > ヘッダーの復元 > 値の更新
定期実行設定
これはお好みで。
利用してみての所感
準備が楽ですね。とにかく。必要な権限設定もスプレッドシートに振っておけば良いので考えることが少ないです。
あと元がスプレッドシートなので、SQLの一部を値参照するようにしたら、「種別ごとのデータの定期取得」なんかもすぐ用意できます。
是非参考になさってください。
参考
BigQueryのSQLにSQLFluffを利用した話
はじめに
私が主担当しているプロダクトは、Cloud SQLをはじめアプリケーションのログはすべてBIgQueryへ集約しており、データサイエンティストチームでなくても、 簡単にデータアクセスが可能になっています(もちろん情報の取り扱いや危険性については周知しています)。
また一方で、プロダクト開発の一貫として、「自分が開発したものは自分で効果測定しましょう」というルールを設けています。 効果測定の際に利用するSQLは、その正誤確認やメンバーの新規参入のために、GitHubでバージョン管理されPullRequestでのコードレビューをするべきです。
「予約語は大文字にしてください」「インデントがズレています」「テーブル名にバッククォートがあるない」、、 これらはレビュイーにとっても指摘してほしい箇所ではないことは自明です。 一方で、効果測定で分析のために発行するSQLは膨大で目grepは難しい。
私の大好きな名著にもありますが
コーディングが常に一定のルールに従って行われる体制作りも大切でしょう。ルールが守られているかどうかは、コーディング中にツールで自動チェックされるようにしておきます
ということで、早急にリンターを入れる必要がありました。
SQLに特化したリンター
ESLintやPrettierなど、プログラミング言語を対象としたコードフォーマッターは有名ですが、SQLに特化したリンターについてはあまり知られていません。 PrettierにもSQL対応されたPluginがあるので、試してみました。
$ yarn add -D prettier prettier-plugin-sql
実行前のSQL
with const As ( select 365 * 2 as interval_date, ) seLect *, fRom project-name.data-set-name.schema-name, const
prettierの実行
$ yarn prettier --write db.sql
実行結果
with const As ( select 365 * 2 as interval_date, ) seLect *, fRom project - name.data - set - name.schema - name, const
参照先のテーブル名及びプロジェクト、データセット名はクォートで囲まれていないと文字列扱いになり、改行されてしまいます。
当然、BigQueryのコンソール画面上でもエラーが出てしまいました。
また仮に、参照先をバッククォートで囲った場合、インデントの調整はされるが予約語の統一はデフォルトではされないようでした。 (設定ファイル書けばいいんでしょうけども正直面倒。。)
with const As ( select 365 * 2 as interval_date, ) seLect *, fRom `project-name`.`data-set-name`.`schema-name`, const
SQLFluffの発見
代替となる良い方法はないものかと探していたところ、SQLFluffなるものを発見。さっそく使ってみます。 実行環境は以下です。
Python 3.9.7 pip 21.2.3
sqlfluffのインストール
$ pip install sqlfluff $ sqlfluff --version sqlfluff, version 0.13.1
設定ファイルを配置
[sqlfluff] dialect = bigquery
$ sqlfluff lint db.sql == [db.sql] FAIL L: 1 | P: 6 | L045 | Query defines CTE "const" but does not use it. L: 1 | P: 12 | L010 | Keywords must be consistently lower case. L: 2 | P: 5 | L036 | Select targets should be on a new line unless there is | only one select target. L: 3 | P: 33 | L038 | Trailing comma in select statement forbidden L: 6 | P: 1 | L010 | Keywords must be consistently lower case. L: 6 | P: 1 | L044 | Query produces an unknown number of result columns. L: 7 | P: 6 | L038 | Trailing comma in select statement forbidden L: 8 | P: 1 | L010 | Keywords must be consistently lower case. L: 9 | P: 22 | PRS | Line 9, Position 22: Found unparsable section: | '-set-name.schema-name,\n const' All Finished 📜 🎉!
一部解釈できないとのことなので手動で修正して再実行
$ sqlfluff fix db.sql ==== finding fixable violations ==== == [db.sql] FAIL L: 1 | P: 12 | L010 | Keywords must be consistently lower case. L: 2 | P: 5 | L036 | Select targets should be on a new line unless there is | only one select target. L: 3 | P: 33 | L038 | Trailing comma in select statement forbidden L: 6 | P: 1 | L010 | Keywords must be consistently lower case. L: 7 | P: 6 | L038 | Trailing comma in select statement forbidden L: 8 | P: 1 | L010 | Keywords must be consistently lower case. ==== fixing violations ==== 6 fixable linting violations found Are you sure you wish to attempt to fix these? [Y/n] ... Attempting fixes... Persisting Changes... == [db.sql] PASS Done. Please check your files to confirm. All Finished 📜 🎉!1, 1.94s/it] [3 unfixable linting violations found]
実行結果
with const as ( select 365 * 2 as interval_date ) select * from project-name.`data-set-name`.schema-name, const
設定をもっと細かく追加したい場合は、.sqlfluff
にルールを追加します。
リファレンスも直感的でわかりやすいので、ありがたいですね。
[sqlfluff] dialect = bigquery exclude_rules = L034 [sqlfluff:rules:L010] # keyword capitalisation_policy = upper [sqlfluff:rules:L014] # column extended_capitalisation_policy = lower [sqlfluff:rules:L030] # function extended_capitalisation_policy = upper [sqlfluff:rules:L040] # boolean/null literal capitalisation_policy = upper
再度write実行した結果
WITH const AS ( SELECT 365 * 2 AS interval_date ) SELECT * FROM project-name.`data-set-name`.schema-name, const
こちらでやりたいことはできました。
利用してみての所感
分析用のSQLは抽出方法の思考に意識をかなり注力しているので、チェックツールとして機能してもらえるだけでもかなり楽になりました。
一方で、SQLSluffは解析にかなり時間がかかるツールなので、1,000行程度のSQLで10~20秒待たされることもあります。 なるべくプロダクト初期に導入しておいて、huskyなどで差分ファイルのみをチェックするようにしておきたいところです。
また今回は、dialectにBigQueryを指定子ましたが、他にも様々なサポートが用意されているので、機会があれば利用していきたいと思います。
Vue.jsかReact.jsか(2021)
はじめに
現職では複数のマイクロサービスに携わっているのですが、そのうち1つのプロダクトにて、FEの技術選定について現場のエンジニアと会話する機会がありました。
彼から「Vue.jsはもういいです^-^;;」といった反応で、結果React.jsを選択することになりました。
「どういう基準で技術選定してるの?」っていうのはおいといて、チームにとってどちらを選んだら良いのかをちょっと考えてみました。
経験値
Vue.js, Nuxt.js, React.js, Next.jsをSPA, SSRで0->で開発・運用した経験があります。
各々だいたい二年ずつぐらい。
WEBの方々のご意見など
- 開発者ならReact.js, ユーザならVue.js
- Vue.jsは直感的だからいいよ
- Reactのほうが1.2倍楽しい
Google Trends
- vueが急にガクッと落ちてるのはなんなんでしょう・・
- 「検索されている=人気ではない、むしろドキュメントが曖昧で検索している人が多いんだ」なんて意見もありますよね。同感です
世間一般のWEBサイト
Vue.js
都内の最新感染動向 | 東京都 新型コロナウイルス感染症対策サイト
無料ゲーム、オンラインゲームの人気タイトル満載! - Yahoo!ゲーム
React.js
【楽天市場】Shopping is Entertainment! : インターネット最大級の通信販売、通販オンラインショッピングコミュニティ
ヤフオク! - 日本最大級のネットオークション・フリマアプリ
Yahoo!ショッピング - PayPayボーナスがもらえる!ネット通販
【公式】PayPayフリマ - ペイペイフリマ|かんたん・安心フリマアプリ
Amazon | 本, ファッション, 家電から食品まで | アマゾン
※下記で適当に調べているので、間違ってる可能性はあります
Find out what websites are built with - Wappalyzer
- ザーッと探しただけですが、Vue.jsよりもReact.jsを採用しているサイトのほうが多そうな印象でした
- ヤフー系列はReact.js優勢なのか?と思いきやVue.jsがあったり
- やっぱりVue.jsはコロナ対策サイトの印象が強いですね
感想
自分もこの方のコメントの通り「VueはeasyでReactはsimple」というのがしっくりきます。easyだからこそ取っ付き易く、コロナサイトもOSSとしてちゃんと機能し始めた(Vueだからチャレンジしてみよう、少しでも貢献したいと思えるエンジニアが出てきた)のかなと思います。
サイト規模がスケールするならReact、しないならVueでいいかなと。どちらもコンポーネントの設計を正しく行っていればスパゲッティなコードも生まれないし、テストも綿密に書けるはずです。
スケールするときを想定してatomic designに沿ってるか、とかrepository pattern採用してるか、とかstore(redux/vuex)を正しく使えているかとか、JavaScript Framework外のことのほうが大切だと思います。