育休中に基本情報技術者試験に合格した

タイトル通り、育休中にチビチビ勉強して10月末に試験を受けて合格した。紙時代(5年以上前)に一回受けているが、申し込みして勉強せず行ってしまったのでノーカンということで…。

A 720 B 665点で合格した

受験動機

夫(非IT職)が「ITパスポート受けようかな〜」と言い出して勉強し始めたため。

今までは割と資格なしでやれちゃっていた。資格試験勉強が嫌いなこともあって避けていた。しかし、非本職の人が国家資格取ってるのに本職の人が無資格でいいのか?!と思った…というしょーもない理由で受けることを決めた。

それと出産後に簡単な計算もできなくなっていて、このままじゃ復帰後まずいと思ったため。人間、負荷がかからないとすぐ衰えるので生物としてよろしくない。

勉強期間

  • 5ヶ月(1日1〜1.5時間程度)

産後5ヶ月過ぎに勉強を開始した。それより前は勉強どころではなかった…。

赤ちゃんさんの昼寝は30分×2回だったので、片方の昼寝でダラダラ or 家事をやり、計算など集中が必要なものは夜1時間ほどやっていた。夜そのまま寝ると記憶も定着しやすかったように思う。

限られた時間で本番に近づけるため計算は簡単なものも紙に書いて、暗記ものは電子画面のみで解くようにした。Aに8割、Bに2割くらいの時間を割いた。

使用教材と学習方法

科目A

  • いちばんやさしい基本情報技術者(本とiOSアプリ)

kihonjoho.jp

立ち読みして図と解説を見てから選んだが、過去問道場やレビューを見るとあまり評判が良くなかった。「〇〇は出ません」と書かれている箇所が多々あるが、そうは思わなかった。黒字まで含めて理解に努めた方が良い。

まずはアプリを使い過去問を全て解いた。アプリで苦手分野を確認できるので、その分野のテキスト例題を解く→過去問を解く…を繰り返した。過去問道場の掲示板に「過去問で見たことがない単語(=新シラバス用語)が出る」とあったので、過去問ばかりに頼らずテキストも読んだ。ChatGPTも活用し、全体的に理解不足がないようにした。

基礎理論が一番苦手だったので、中学数学・高校数学の参考書も買って対応する単元の問題を解いた。特に平方根はすっかり忘れていた。科目Bでも出てきたので復習した。集合も問題によく出るのでこちらも復習した。

科目B

  • 出るとこだけ!基本情報技術者[科目B]テキストと問題集
  • いちばんやさしい基本情報技術者(iOSアプリ)

www.shoeisha.co.jp

プログラム経験者でも、擬似言語の仕様に慣れた方がいい。配列とfor文の添字が1始まりからなので、演習の始めは間違えることが多かった。テキストでは演習量が足りないので、問題集もあると良い。

自分はオブジェクトや単方向リストは業務であまり触らないので、その辺を重点的にやった。

参考書ではトレース解法を激推ししているが、素直に信じて全問トレースで解くと間に合わないだろうなと感じた。簡単なものはプログラム読解で解けないと時間が足りない。

また、夜の時間を使って70分以内に通しで16問解けるか確認した。制限時間を設けておかないと、本番で焦ってしまうため。

セキュリティは過去問を全問正解で解けたので、大して対策しなかった。普段のセキュリティ研修で出てくるような内容も多かったので…。

試験時の時間配分や進め方

科目A

1周目は30分で解き終わった。10分ほど小休憩した後、チェックをつけたものを中心に見直した。見直しチェックがつけられるので、即答できないものはマークして次の問題に行くようにした。

科目B

最初の4分で全問題確認し、擬似言語の中から解く12問を決めてチェックをつけた。解かないと決めたものはこの時点で適当に答えを入れてなかったことにした。その後14分でセキュリティを全部解いた。残りの50分で擬似言語12問を解いた。

本番は焦ってしまうので、100点ではなく合格点を目指す戦略の方が落ち着いてやれそうだと思ったため。結果的にうまく行ったように思う。

感想など

CBT化後に一回で合格できたのは素直に嬉しかった。やった〜🥳蓋を開けてみるとAの方が得点が高く、かけた時間に比例して成果も出るのだなと感じた。Bはギリギリを攻めすぎた感がある。もう少し過去問をやっていれば得点できそうだった。

よく「基本情報技術者試験なんて簡単だよ〜」とか言われているが、ほんとか?と思ってしまった。応用情報技術者を勉強してみると、確かに基本情報は簡単だと感じる。しかし、ソフトウェア開発経験者でもしっかり勉強しないと落ちそうだ。実際、自分の業務範囲はできるがそれ以外はさっぱり、というところからスタートだった。

試験を通してネットワークやセキュリティなど、ちょっと関わるけどあまり自信がない分野の知識を勉強できたので良かった。自己学習だと身についたかわからないので、試験で合否が出るのはありがたい。目標があると隙間時間に少し頑張ろう!という気持ちになる。

隙間時間に勉強する習慣がついたのも良かった。せっかくなので高度まで目指してやってみようと思う。次は応用情報にチャレンジしてみたいので勉強を始めた。

リモートワークでOJTは成立するのか

最近世の中全体がフルリモートから出社回帰になっている1。朝電車に乗るととても混んでいるし、帰りもそこそこ混んでいる。 それに合わせてなのか、リモートワークでジュニア層の育成はできないとか、できるとか結構記事を見るようになった。

自分は2年ほどジュニア層の面倒を見たので、どう思うかまとめておく。未経験でフロントエンド領域のソフトウェア開発をする業務を与えられた新卒の面倒を見る、という前提である。あまり職種は関係ないと思うが、前提は共有したほうがいいと思い書いた。

ちなみに、最初の1年は出社が非推奨寄りの時期に被っていたので、リモートで育成経験があると言えるはず。

OJT自体はリモートワークでもできる

自分の意見は次のようなものだ。

  • リモートでもOJT自体はできる。ただしジュニア層が1人で仕事できるようになるかは話が別

リモートでもOJTはできる。決まったやり方で進捗報告をさせ、進みが悪ければビデオ会議ツールを使ってペアプログラミングすれば良い。技術的な知見は大体Webページのどこかにあるので送れば良い。進捗報告はチケットでもSlackチャンネルへの投稿でもなんでもいい。更新がなければ間違いなく進捗が悪いので、こちらから働きかけて強制的にペアワークしてしまえば仕事は終えられる。

振り返りなども仕事終わり、または定期的にやれば良い。ビデオ会議ツールと同時更新できるドキュメントツールを使い、書き込みながら内省を促す。なのでOJT自体はできる。

どのタイミングで問題に気づくか

しかし、問題はある。OJT期間ではなく、OJT期間が終わった後に出てくるのだ。 まずOJT期間中に自分から状況を報告し、悪いときにすぐアラートをあげる習慣が身につかなかった場合、リモートワークでは間違いなく壊滅する。理由は簡単でOJT期間中に育成担当者が能力不足をフォローしてくれていた分がなくなるからだ。

OJTが終わっても面倒を見続ける職場もあるかもしれないが、いつまでもずっと育成担当者が1から10まで尻拭いをしてくれるわけではない。どこかで必ず「ハシゴが外れる」ときがやってくる。

そこにリモートワークが重なると、自主性がない状態で監視もなく放り出された状態になる。最悪プロダクト開発 or プロジェクト開発の進捗報告まで「何もできてませんでした」の状態になる。当人もわかっているので焦ってなんとかしようとするが、1年程度の経験でできることは少ない2。しかし自分からピンチである報告ができないので進捗報告の場で注意される or 注意はされないが誰かに仕事が再割り当てされるようになる。

また、ツールの使い方や調べ方も「送られてきた情報を元に自分でさらに調べる」人以外はあまり身につかない印象がある。全部やり方を教えてもらうのが当たり前で、自分ができないのはやったことがないのに方法がわからないせいだ、のような考えがベースにあることが多いからだと考えている。教えてもらえる、答えがあるのが当たり前の環境であれば、自分で情報を探してきて推論する・試す行動にはなりにくい。

上記が組み合わさった結果、主体的な人は手助けなしでどんどん上達するが、そうでない場合はずっとジュニア層のままという状態になる。 割合としては、主体的な人:それ以外 = 2:8くらいな印象を受ける。

主体性を他者が育成できるのか?

じゃあ主体性を身につけさせれば良いだろうと考える。ソフトウェア開発における主体性は次のようなものが挙げられる。 これをOJT期間中に身につければリモートでも成立する。都度考え方を伝えてやってみせれば良い。

  • 状況が悪い時は自分から報告する
  • まずは自分で調べる・情報を探す
  • ドキュメントや文章を読んで理解を試みる
  • 文章を読むだけではなく、動作させてみる
  • 成果物の内容は説明できる(例:なぜその実装にしたのか?)

で、実際やってみてどう思ったか?結論は無理、である。 そもそも新卒の時点で20から26年ほど生きてるのだ。考え方や思考の仕方を第三者があれこれ働きかけたところで変えられるわけがない。 できるなら宗教でも開いて教祖をやったほうが儲かりそうだ。

対面だろうがリモートだろうが、「怒られたくない / 失敗したくない。だからやり方がわからないとやりたくない / やれない」みたいなのをひっくり返すのは難しい。ジュニア層ができる仕事は既存のコードを真似すれば動くようになる仕事か、ある程度手順を分解して書かれた状態のものばかりになる。すると、誰かが作業分解する手間が発生する。結果人は増えても戦力は増えない、とジリ貧になる。

お金を貰っているのだから成果物に責任を持つ。成果物を仕上げるのに必要なものを自分で準備する、みたいな価値観はもう時代遅れのものになっていると感じる。そのメンタリティがないことを指摘すると(実際は意味が違うのだが)心理的安全性が損なわれた、とかブリリアントジャークだ!とか言われてしまうリスクがある。パワーハラスメントだと言われるリスクもある。

誰かが作業者ではダメだ、と指摘しなければずっとそのままになる。しかし一体誰がこんなハイリスクなことをやりたがるのだろうか?自分の仕事もやりながら、メンタリティの指導も行いジュニア層ができない仕事もフォローする必要がある。+100万貰ってもやりたくないなと思ってしまう。

出社させて背中をみせれば良いのかもしれないが、育成する側には負担がかかるだけでデメリットしかない。そもそも同じ空間で一緒に働いたくらいで人の行動や意識が変わるものだろうか?無理では?

まとめ

リモートだろうがオフィスだろうが、結局ジュニア層の育成がうまくいくかはジュニアメンバー本人次第である。 指摘や指導にリスクがある時代になってしまったので、自分で内省を繰り返して改善するしかない。

もう今の時代、育成担当は断った方が良い。育成担当になってしまったら当たり障りなく仕事を手順に分解して振り、主体性があるかを早めに見分ける。主体性があれば何も言わなくてもこちらに働きかけてくるので見分けられる。

もしなさそうであれば作業者として割り切って付き合う。仕事は全て作業に分解して渡し、ある程度放置できる状態にしてほどほどに状況確認する。ジュニアメンバーは仕事ができたと感じ、こちらはリスク回避と自分の仕事に集中できる。このくらいが落とし所ではないだろうか。

お金を稼ぐことだけ考えるなら副業や株など、正社員以外で稼ぐのがコスパがいいんだろうなと思う3


  1. 自分の所属している会社は週一は出社推奨になっているが、ほとんどリモートワークできる。
  2. もちろん、外れ値でなんとかできる人もいる。レアケースなので一旦除外して考える。
  3. 職場が好き・マネージャーをやりたいのであれば率先してやった方が良いと思う。

スプラトゥーン3をやっている

昨年、ニンテンドーカタログチケットの期限が来てしまうけどあまり取り替えたいものがない状況になった。あまりシューティングゲーム、しかもオンライン対戦ありのゲームは忙しくて好きではないので避けていた。しかし、なんか有名だしイカちゃんが可愛いからという理由でスプラトゥーン3に交換した。

www.nintendo.com

ゼノブレイド3発売前に交換し、少し遊んでゼノブレイド3が来たので放置していた。しかしせっかくイベントもあるし、サーモンランはやってみたかったので2月あたりから少しずつ遊び始めた。今はサーモンランと追加コンテンツのサイドオーダーを中心に遊んでいる。

先日やっとでんせつ帯に上がれたので、ある程度遊んだ感想をまとめておく。

これは直近成功したバイト。まずは倒されないようにしたい

思った以上に色々な遊び方ができる

勝手に通常のナワバリバトルと強いランクマッチ(Xレート?)しか遊べないものだと勘違いして避けてしまっていた。ちょっと勿体なかった。サーモンランはCPU(シャケ)相手の協力ゲームで別物だし、一人モードもタイムが記録されてRTA的にやり込むことができる。サイドオーダーはビルド要素があり、これだけでも色々工夫して「どんなビルドにしようかな」と試行錯誤できて面白い。

サーモンランばかりやっていると、お金が余ってしまう。ただ、イカちゃんに可愛い服(ギア)を着せてあげようと思うとお金がいる。なんか星みたいなランクがあり、買い替えてバトルに強い補正をかけられるが「星付きの服を着せてあげる」みたいなモチベーションで取り替えてみている。

ステッカーとか置き物をロッカーに飾ることもできる。ロッカーはナワバリバトルをしないと大きくならないので、たくさん飾りたかったらちょっと我慢してやらないといけない。まあ必須ではないし、持っているだけでも嬉しいのでコレクター的な要素もある。

写真を撮ったりするだけでもかわいくて満足できる。あとお絵描きをして投稿できたりして、Nintendo Switchですごい絵を描いてる人とかいてすご!と思うし、描けなくても見せていただくだけで楽しい。

こういう感じのエモ写真とか撮れる

食わず嫌いしないほうがいいなあと思うのであった。

一人用モードとYoutubeで上手くなる

自分はアクション・シューティングゲームは苦手で3月後半まではサーモンランも低レートだった。たつじんに上がれればラッキーくらい。とにかく処理も納品も全然できないし、スペシャルの使い方もあまりわからなかった。オルタナという元々ある一人用モードはほとんどやったけど、これだけだとあまり理解できてなかったようだ。敵の数が全然違うから参考にならなかったのかもしれない。

Splatoon3 - スプラトゥーン3 攻略&検証 Wikiを見て種類などは覚えたが、やはりそれだけでは実際どう動けば良いか分からなかった。

なのでYoutubeで上手な方の動画を拝見して、動き方やスペシャルパウチの使い方を真似するところから始めた。ポイントはただ遊ぶだけの動画ではなく、しっかり自分が今、なぜそうするのか?を解説している動画を見ることだった。参考にさせていただいているのはこのお二人の動画。オンラインゲームは味方のせいにしたりすることが多いが、人の悪口とか暴言とかないのが良い。見ず知らずの人に暴言吐いちゃうのとかちょっと疲れてるんじゃないか、休んだほうがいいよ…と思ってしまうので。

www.youtube.com www.youtube.com

あと、サイドオーダーの敵の出現方法がサーモンランと似ており、これもよかった。大量の敵に囲まれながら生存してチャンスを掴む、という動きが練習できた。苦手なブキ種もサイドオーダーで30Fまで登り切れるとやられた数が減っており、ブキ種に慣れることも重要だと気づいた。上手くなるにはある程度時間をかけて「とにかくひたすらやってみる」のが必要だなと思った。

バイトも失敗はあるが、失敗を恐れず良いとされる動きを試す。イカリング3を使って結果をすぐ振り返り、改善ポイントを探す…これで少しずつレートが上がるようになった。難しいと言われるシフトだろうが、毎回少しでも参加して継続することを意識した。

するとたつじん帯だったら少しはキャリーもできるようになって、でんせつ帯に上がれた。キャリーしてもらったからだろうと思い、一度たつじん3まで下げてからまた上がれたので、まあいいかなと思った。

あと、でかいオカシラシャケなんか一回も討伐できたことがないのに、たまに倒せるようになった。でんせつ帯ではまだキャリーしてもらう側になってしまっているので、もっと練習して足を引っ張らないようにはなりたい。目標は高めにおいて、評価のカンスト(999レート)を目指すようにしたい。

結構嬉しかったので写真を撮った

サーモンランは仕事と人生を教えてくれるゲーム

で、これって仕事が上手くなるために必要なことと一緒だなと感じた。最後はご時世的に推奨できないが、強い人は四六時中そのことを考えており、差がつくのは仕方ないですよという点では一緒だなと思う。

  • 上手くなるには上手い人の思考を見せてもらう
  • 見せてもらうだけでなく自分で真似る
  • 悪い結果から逃げずに改善点を探す
  • 選り好みせず何でもやってみる
  • 継続する
  • 似たような環境で練習する(業務外に勉強する)

サーモンランでは、他にも「人と同じところを見ないで分担する」ことの重要性が挙げられている。インク量を適切に振り分けてシャケを倒すことで安全になるから。これもに仕事と一緒で、一定の処理能力がある複数人で分担することで大量の仕事をこなせると言い換えられる。処理能力がないとサーモンランではシャケに倒されてしまい、逆に迷惑をかける。この辺も仕事と一緒で…えげつないゲームだな。スプラトゥーンから仕事を教えられるとは思っていなくてびっくりした。

プロバイターになれるわけではないが継続しなければチャンスは来ない、という点は人生と一緒だと思う。なんか可愛げのある見た目をしているがすごいポテンシャルを持ったゲームだ。考えすぎかもしれないが自分はそういう印象を受けた。

その他

基本はZRで打ち続けるゲームなので、手が痛くなって長時間遊ぶことはできない。目も疲れるし。これが程よくやめられるタイミングになっていてよかった。今のところ日々の原稿や学習とも両立できている。大人だしね!

あと、自分は仕事後も心配事や気になることがあると引きずってしまう。スプラトゥーンはそんなことを考えているとすぐ倒されてしまうので、完全に忘れられるのも良い。ちょっと気持ちの切り替えができるようになった気がする。RPGとかだとターン制で余裕があってあまり忘れられないので、こういう展開が早いゲームもいいのかもしれない。

Slackのtimesチャンネル文化が好きじゃない

speakerdeck.com

はてなブックマークやxでこの資料が話題になっていた。80%くらいは同意できるが、Slackの部分は個人的にはうーんと思った。特にtimesが好きではなくて、「timesじゃなくてチケット管理システムを使え」と思ってしまった。なんで好きじゃないんだろう?と思ったので整理しておく。

情報が垂れ流しだと探しづらいから

timesには思考や調べたことを投稿して、後から見返せるようにしましょうという役割がある。でもそれ、本当に見返せるのだろうか?Slackの検索クエリはGoogleほど絞り込みが効かないし、部分一致の検索でもかなりフィルタリングされた情報がヒットする印象がある。本当に探し出せる気がしない。

また、投稿した人ではない誰かが仕事を引き継いだときに困るんじゃないか、という思いが拭えなくて好きじゃない。例えばエンジニアの退職でリポジトリのメンテを引き継ぐことになったとき、調査内容がtimesに垂れ流しされていたら引き継ぎ側は探せるんだろうか。退職したら関連チャンネルは全削除です〜なんてルールだったらまず無理だし、timesのいつぐらいにある?とか情報がなければ検索でヒットさせるのに時間がかかると思う。そもそも引き継ぎしたばかりではコンテキストがわからないんだからクエリを作れない気がする。

それだったら、チケットシステム(GitHub IssueとかBacklogとかJiraとか)にちゃんと情報を集約して書いて、調査状況が変わったら都度更新する方がよっぽどマシだと思ってしまう。メンバー交代があってもgit commitから辿ることができる1。それに、情報を探す時間も省ける。

Slack検索してる暇を開発やその他の時間に当てた方がよっぽど効率がいいのではないか、と感じてしまう。同じ理由で参考URLも全部チケットに集約して欲しい。次やってきた人はどーすんの?と思ってしまう。

仕事の状況は専用のツールで管理すべきでないかと思うから

仕事のTODOをtimesに書いて、状況が変わったら投稿すると助けてくれる。そんないいことが書いてある。でもそれ、同業者以外の人は困るんじゃないか?何らかのマネージャー職にそんな暇はあると思えない。進捗を一番知りたいのはマネージャー系の職務を持つ人では?

となるとチケット管理システムで進捗状況がわかるようにマメに更新するのがみんな幸せになるんじゃないのと思ってしまう。

察して状態になるのが好きじゃないから

不明点を書いとけば助けてくれるよ、みたいな他力本願な姿勢が好きじゃない。困っていることがあるならチケットに調査状況を集約して、そのURLをSlackに添付して質問に行けば終わるんじゃないのと思ってしまう。人に自分の状態を察してもらって手助けしてほしい、なんて本当にプロの態度なんだろうか?

プロだったら仕事を終わらせることに注力すべきで、聞かなきゃ進まないんだったらさっさと自分で聞きに行けと思う。こういうのがあると状況が悪くなったときに隠すみたいなのが冗長される。だから察してコミュニケーションが好きじゃないんだと感じる。

タバコ部屋っぽくて好きじゃないから

タバコ部屋はタバコを吸ってる人はそこで話を決めちゃって、吸わない人は意思決定に関与できない。だから嫌われるというやつ。timesチャンネルで議論して方向性を決めてコミュニケーション促進ってやつと何が違うのだろうか。結局そこのtimesチャンネルがタバコ部屋の代わりになってるだけじゃんと思う。

なんでタバコ部屋はダメでtimesはいいのか?それ自分に都合がいいからだけなのでは。なんかせこいよな〜と思う。

Slackに張り付いてないでまずはお前の仕事を終わらせろやと思うから

100人いたら100times見るのか?1timesに30投稿あったら一体何分使ってその投稿を追いかけるのだろうか。返信して盛り上がってもいいけど、肝心の仕事は終わるのか?と思ってしまう。期日ギリギリ、または間に合わないならそんなことしてる場合じゃないだろう、と。

与えられたタスクを仕上げて残り時間でダラダラするならわかる。自分もビルド待ちのときは社内資料のURLや関係ないSlackチャンネルを除いて状況把握とかやっている。でもそれはやるべき作業チケットがレビュー待ちなり、終わるなりした後である。times好きな人はtimesが一番、仕事は二の次という印象があって「いや、働けよ」と思ってしまう。

お前はどうしているんだ

ならお前はどうしてるんだよ、という話になる。自分はGitHub Issueを使ってタスク管理する環境なので、全てGitHub Issueに集約している。チェックボックスにすればTODOリストになるし、GitHub Projectsとも連動できるのでこれをマメに更新して「チェックのつき方でどこまでいったかすぐわかる」ようにしている。

github.com

これは仕事全く関係ない個人リポジトリだが、基本の形は仕事とはそんなに変わらない。仕事だともっと文章構造にも気を遣う。例えばエラーログなどの補足情報は折りたたんだり、コードブロックはハイライトしたり、という感じ。決定的に変えているのは調査内容のまとめを作ってIssueの先頭コメントに書き戻す、またはURLリンクを貼ること。これは個人だとめんどくさ満足して終わらせてしまうが2、仕事の場では「次の人はそれでは困る」ので考察や結果をしっかりまとめて書き残している。

後から来た人が困らないように情報を集約して困らないようにしたい、自分が過去とても困ったから。というモチベーションでやっている。

所感

timesでプロの仕事が成り立つのは次の要件を満たしたときだけで、そりゃ何でも成立するよと思う。ジュニアではない = 0から手助けなしでチケットをクローズできる人とする。

  • ジュニアメンバー、特に要介護状態のメンバーがいない
  • 仕事を期日通りに仕上げるのが普通の人しかいない
  • 仕様や障害など、クリティカルな内容はオープンな場ですぐ議論できるほどのリテラシーがある
  • 状況が悪くなった場合は隠さずすぐ報告できる

下3つは当たり前の話だが、3つを満たしてる人には中々出会わない。特に下2つ。どちらか欠けていることが多い。過去の職場3のどこにでもいたので、もう職場環境問わずいるものだと考えている。ただ、職場によってはtimesが成り立つ人が多いところと少ないところがあるので、職場レベルが高いところはしっかりスクリーニングされてる印象がある。スクリーニングされているところならtimesでいいんだろうな。

誰かtimesで困ってない会社リストとか作ってくれたら転職先の参考になるんじゃないだろうか。自分がそこに行けるかは別として。

参考URL


  1. コミットにチケット番号を書くルールなら絶対探せる。なくてもmerge記録から逆引きできる。
  2. 代わりに個人での検証はブログにしてまとめているからいいかなと…。
  3. 過去記事を読んで欲しいが何回か転職経験がある。お前の会社レベルが〜とかではないかなと思う。思いたい…!

フィードバックを早く受けた方か早く終わる

雑感です。実力は同程度、または仕事が終わる人の方が能力値が低い状態を想定しています。

仕事が終わらない人の特徴

1回目のレビューが遅い。〆切ギリギリに出てくる。仕事がほとんど終わった後に初めて出てくる。

やり直しの幅が広いので直すのに時間がかかる。「何でそうするの?」の質問に答えられず詰められる。考えたときのことを忘れているので思い出さないといけない。

取り繕ったりすると矛盾が出て突っ込まれる。信頼度が下がってチェックが厳しくなる…。

という感じで悪循環が永遠に続く。

仕事が終わる人の特徴

次のタイミングでレビューを受ける。

  • 取り掛かる前
  • 進捗が1/3程度進んだとき
  • 詰まって質問後、教えてもらった物を直したらすぐ
  • 不安なところが出てきたとき。15分程度で考えをまとめてから

とにかくやることや意図をまとめてすぐ見せる。じっくり見てくれる相手がいなくても「見える状態には」する。

戻しや方針転換が少ないので無駄が少ない。詰まっている時間も相対的に短い。

レビューする側も思いだすのに時間がかからない。レビュー待ち時間も減る。

—-

始めのうちは「全部1人でやったけど〆切に遅れました」は全然偉くない。むしろ頼める仕事が減ってマイナスである、という認識が必要かもしれない。

仕事が早く終わる方が経験値が溜まり、実力差は1ヶ月程度でひっくり返ってしまう。抱え落ちは避けるようにしたい。

進捗が見えることで信頼感があるので、レビューも緩くなる(品質は担保されている前提)。

未経験の状態から5ヶ月で開発できるようになるために必要なこと

正確な時間は忘れたが、そういう状態になったことがあった。 ある程度自力(ベッタリではなく、短期間でレビューを挟む程度で良い)状態に持っていくには何が必要だったか整理してみる。

最初に結論

数字が若い順に最優先で何とかする。大前提として本人が改善のための努力を惜しまないことがある。他責思考ならもう諦めて作業者として割り切った方が良い。

  1. 日本語の読み書き
  2. ツールの使い方(特にタイピング)
  3. 仕事の流れを一通り把握する
  4. 時間の使い方
  5. 質問の仕方
  6. テスト駆動開発(TDD)の考え方
  7. 学習習慣

1. 日本語の読み書き

リモート・オンライン問わずチャットやチケット(Issueなど)ツールの日本語文章に注目する。ここが怪しい場合、論理的に仮説立てて実装するどころではない。

助詞・漢字の使い方が間違っている場合、読み書き能力が低めであると仮説立てられる。ちなみに文章を正しく読み上げられないも怪しいポイントになる。

業務時間を使ってでも最初になんとか改善してもらいたい点。読み書きは文章の要約を使って練習すると良い。「ふくしま式「本当の要約力」が身につく問題集」はとてもおすすめ。最初に主語・述語の見分け方から始めてステップアップできる。大人でも後半は難しく感じる。

2. ツールの使い方(特にタイピング)

エディタやチケットツール・カレンダーなど、普段仕事で使うものの使い方は一緒に作業して習得させておく。ここがおぼつかないと全ての作業に時間がかかる。時間がかかると余裕がなくなり、仕事どころではなくなってしまう。

特にタイピングは練習させた方が良い。考えることができてもタイピングが遅い・打ち間違えてしまうとそれだけで脳内リソースを消費するため。

3. 仕事の流れを一通り把握する

まずはあーだこーだと言う前に、一つチケットを割り当ててペアワークでやってしまうと良い。試行錯誤してもらいたいところだが、何もわからないと全く前に進まず時間を浪費してしまう。1

最初はトレーナー側の時間を奪われたように感じるが、作業分担できる方が後から圧倒的自由を手に入れられる。

4. 時間の使い方

日本語の読み書きが怪しいと大体時間を効率良く使えないことが多い。差し込みが入らないように気を使うことはできるが、年次が上がるとそうもいかない。計画の立て方・一日の時間の使い方・詰まったときに時間を浪費しないようにする方法を伝えて身につけさせたい。

なぜ、あなたの仕事は終わらないのか スピードは最強の武器である」は考え方として重要だと思う。全て適用するのは難しいが、「2割の時間で8割終わらせる」考え方を知って実践できるかどうかでだいぶ変わるように感じる。

また、「全ての仕事はやり直しになる」という考え方も身についていると、抱えずに早くフィードバックを受けにこようとなる。すると改善が繰り返されるので伸びやすくなる。

5. 質問の仕方

これも結構大事で、絶対わからないことはあるので質問を0にはできない。応用系は人に物を依頼する、となる。できないと今後困ってしまう。トレーナー以外の人に質問させる機会があれば、積極的にペアワークして「こういう風に聞こう」ということを見せていきたい。

質問文は一緒に考えつつ、「なぜこれを伝えるのか?」「なぜ最初に要件を言うのか?」などを説明していく。魚を獲って、次に釣り方を伝授するようなイメージ。昔は釣り方を教えるのが重要とされていたが、タイパな時代なのでまず問題を解決する方が聞いてもらいやすい。

ひよこエンジニアのためのお仕事サバイバルガイド2023」にも色々記事をまとめておいたので勧めるといいかもしれない。

6. テスト駆動開発(TDD)の考え方

まずはTODOを立てて必要な物を整理し、テストを落として通す流れは初心者ほど受け入れられやすい。確実に前進するし、何より「このTODOが終われば終わりに近づく」安心感が違う。

ゴールを見える化して手戻りをなくす、と言う点が心を折れにくくするのだと感じる。できるようになってからTDDではなく、最初からTDDで進める。TDDしにくいときは割り切る、くらいでちょうど良い。

テストを書いて落として通す、ができるようになるとデバッグ時も慌てなくなる。すると一気に実力が伸びる印象がある。エラーやテストの失敗に慌てなくなるのが効果として大きいのではないか。初学者だと上手くいかないことがあるとすぐ慌ててしまうため。

やはり「テスト駆動開発」を使って教えるようにしたい。本を読ませるのは結構大変なので、ルールだけでも覚えてもらえればいい感じはある。

7. 学習習慣

6までできれば放っておいても学習するようになる。自分の領域の名著などをお勧めして心を折らないようにだけ注意したい。慣れない領域だと、初学者向けの本でも精一杯だったりする。何か持っているならそれに目を通す頻度を上げてもらうだけでも充分である。


  1. これは実体験からくる感想で、結局あまり身に付かず申し訳ないことをしたなと感じる。反省。

Testing LibraryとJestを用いて親子コンポーネント間のイベント発火をテストする方法

UIコンポーネントからイベント発火したことをテストする

親子コンポーネントの連動をテストする際、子コンポーネントでイベント発火したことを親コンポーネントに伝えたか確認したいことがある。特に小コンポーネントで状態を加工して親コンポーネントに送る場合は値もテストしたい。

基本の考え方

親子間でコンポーネントのイベント発火に応じた連携を行う場合、次のような流れで動いている。

  1. 親コンポーネントからイベント発火後に呼び出す処理をpropsしておく
  2. それを小コンポーネントのイベントハンドラーにセット
  3. 小コンポーネントでイベント発火すると1が動作する

テストを書くとき問題になるのは1である。まず、親コンポーネントがないのにどうやって発火後の処理を再現すれば良いのか?と考えて詰まってしまうためである。次に3が問題となる。これはイベント発火を検知するマッチャーがわからず詰まってしまうため。順に解決できればテストを記載できる。

1はjest.fn()1というAPIを使って解決する。これはモックオブジェクト(実際の関数や依存関係を擬似的に再現するオブジェクト)2を作成するJest組み込みAPIである。つまり、親コンポーネントはないのでテスト用の関数を仮でセットしておけば良いのである。

コンポーネントのレンダリング前に、イベントハンドラーで発火する関数のモックを用意する。それをpropsに割り当てる。 実際に記載すると次のようになる。

  it("チェックボックスをクリックすると親コンポーネントへイベント発火状態を伝えること", async () => {
    // イベント検知のセットアップ
    const event = userEvent.setup();
    // イベントハンドラーのモックを用意
    const handlerMock = jest.fn();
    // propsにモックを与えて「親コンポーネントがあるように」見せかける
    const content = render(
      <AgreementCheckbox onCheckboxStatusChanged={handlerMock} />
    );
  });

jest.fnhandlerMock変数に置くのがポイントとなる。これをpropsに割り当てたり、マッチャーで用いたりするため。

イベント発火自体をテストする

テストするためにはuserEventを使い、イベントを発火させる。その後toHaveBeenCalled()3マッチャーを使い「イベントが発火したか」をテストする。検証の対象はセットアップ時に用いたjest.fn()オブジェクトとなる。

// (省略)
// チェックボックスをクリックする
await event.click(content.getByRole("checkbox"));
// イベントハンドラーが呼ばれたことを確認
expect(handlerMock).toHaveBeenCalled();

イベント発火時の引数をテストする

イベント発火時に親コンポーネントへ値を送信するとき「意図した値が送信されているか」もテストしたくなる。そのような場合toHaveBeenCalledWith()4を使う。toHaveBeenCalledWith()の引数に送信する値をセットして検証する。

// (省略)
// チェックボックス状態が親コンポーネントに送信されることを確認
expect(handlerMock).toHaveBeenCalledWith(true);

サンプルコンポーネントとテスト

コンポーネント

import React, { useState } from "react";

type AgreementCheckboxProps = {
  onCheckboxStatusChanged?: (event: boolean) => void;
};

export default function AgreementCheckbox({
  onCheckboxStatusChanged,
}: AgreementCheckboxProps) {
  const [checked, setChecked] = useState(false);

  const handleChecked = (event: React.ChangeEvent<HTMLInputElement>) => {
    setChecked(!checked);
    onCheckboxStatusChanged?.(event.target.checked);
  };

  return (
    <div>
      <input
        type="checkbox"
        name="agreement"
        onChange={handleChecked}
        checked={checked}
      />
      <label htmlFor="agreement">同意する</label>
    </div>
  );
}

テスト

  it("チェックボックスをクリックすると親コンポーネントへイベント発火状態を伝えること", async () => {
    // イベント検知のセットアップ
    const event = userEvent.setup();
    // イベントハンドラのモックを用意
    const handlerMock = jest.fn();
    // propsにモックを与えて「親コンポーネントがあるように」見せかける
    const content = render(
      <AgreementCheckbox onCheckboxStatusChanged={handlerMock} />
    );
    // チェックボックスをクリックする
    await event.click(content.getByRole("checkbox"));
    // イベントハンドラが呼ばれたことを確認
    expect(handlerMock).toHaveBeenCalled();
    // チェックボックス状態が親コンポーネントに送信されることを確認
    expect(handlerMock).toHaveBeenCalledWith(true);
  });

Storybook 8.xのStorybookファイルをTesting Libraryで使用する

UIコンポーネントのカタログツールStorybookを使い、コンポーネントをセットアップする方法もある。StorybookはStoriesというファイルに「どのようなコンポーネントをStorybookに描画するか」を記載する。propsに応じて描画内容を出し分けることもできる1

このStoriesは次のように記載する。

import type { Meta, StoryObj } from "@storybook/react";
import BlogList from "./BlogList";

// Story自体の設定
const meta: Meta<typeof BlogList> = {
  title: "Table/BlogList", // Storyの区分け / Story名
  component: BlogList, // 使うコンポーネント名
  parameters: {
    layout: "centered", // 真ん中に配置
  },
  tags: ["autodocs"], // ドキュメンテーションあり
};

export default meta;
type Story = StoryObj<typeof BlogList>;

// DefaultがStory名
export const Default: Story = {
  args: { // 与えたいprops
    userList: [
        {
          id: 1,
          name: "user1",
        },
      ]
    }
  }

ポイントはargsの部分である2。このargsStory(UIコンポーネントを状態に応じてレンダリング仕分けたもの)ごとに「どのようなprops」を設定するか?記載する。

Storyで場面に応じたpropsを設定しているのだから、Storyを直接インポートしてテストを書けば二度手間にならずに済む。

また、開発初期段階でUIコンポーネントから先に準備したいときにも役立つ。テストが失敗した場合、Storybookを使えばUIコンポーネントの見た目や動作を実際確認できるためである。これはページを作る前にUIコンポーネントだけテストできるので、「残りはページのみ」という気持ちで取り組みやすくなる。

Testing Library側のインポート方法

StorybookはバージョンアップでダイナミックにAPIが変わる。そのため最新の情報は常にStorybookの公式ドキュメントを参照いただきたい。ここではStorybook 8.xのインポート方法を載せる。

StorybookのAPIcomposeStories()3を使うとStorybookからStoryをインポートできる。

// Reactのインポート
import React from "react";
// render関数のインポート
import { render } from "@testing-library/react";
// StoryをレンダリングするcomposeStories APIのインポート
import { composeStories } from '@storybook/react';
// StorybookのStoriesファイルからStoryを全部インポート
import * as Stories from './BlogList.stories';
// DefaultというStoryをデストラクチャリングで取り出して使えるようにする
const { Default } = composeStories(Stories);

it("Storyをインポートしてテストを動かす例", () => {
  // itブロック外でインポートしたStoryでレンダリングする
  const content = render(<Default />);
  // テキストが存在するか確認
  expect(content.getByText("記事ID")).toBeInTheDocument();
});

まず初めに、テスト対象のStoriesファイルからStoryをインポートする。インポート方法はいくつかあるが、本格的にテストを書く場合は全てインポートする方法が使いやすい。パターン分けしている分は何かしらテストを書いた方が良い場合がほとんどだからである。

インポートした後、composeStoriesAPIを使い、Storyを取り出す。すると、Testing Libraryがテストランナー上でコンポーネントをレンダリングする際Storyを指定できるようになる。propsはStoryのargsで設定しているため、その内容を使ってテストが動く。

Storyを状態に応じて分割しておくと、テストとStory名でどのような場面のどんな内容を検証したいのかを伝えやすくなる。デバッグもしやすい。

メリットが多いので基本的にはStoryを使って状態を表現しておきたい。ただ、StorybookのバージョンアップでAPIの破壊的な変更が入ることも多い。npx storybook migrateである程度機械的に作業すること・GitHub Copilotなども駆使して移植作業は半自動化してやると少しは楽になる。スケジュールが佳境な時にはバージョンアップしない方が良い。

また、過度に依存するとバージョンアップで疲弊するため、Storybook上で動作するテストは書かないようにしている。

Testing Libraryでrender関数を使ってUIコンポーネントをレンダリングする方法は複数ある

テストランナーを動かす前に「UIコンポーネントは今どういう状態なのか」をセットアップしなければいけない。UIコンポーネントが必須のpropsを持つ場合、propsを与えなければUIコンポーネントは動作しない。また、 特定の場面で挙動が変わる場合は特定の場面自体を再現してテストしたい。

コンポーネントの初期状態の設定方法はいくつかある。

UIコンポーネントを直接インポートする

Testing Libraryではrender関数の引数にUIコンポーネントを与えることでレンダリングできる。Reactコード内で小コンポーネントとしてコンポーネントを描画する要領でpropsを設定してやれば良い。

一度変数に取り出してレンダリングする方法

具体的には次のようなコードになる。propsはコンポーネントに直接割り当てる。

it("h1ヘッダーテキストが存在すること", () => {
  // render関数を使ってAboutコンポーネントをレンダリング
  const content = render(<About />);
  // レンダリングされたコンポーネントの中にテキストが存在するか確認
  expect(content.getByText("About")).toBeInTheDocument();
});

contentに一度取り出すことで、複数回要素を取得する場合はcontentにクエリ用APIを書くだけで済む。 ただし、一回しかアクセスしないような場合は記述が冗長になってしまう。簡潔に書きたい場合は別の手段を取ると良い。

自分は基本的には変数に取り出す方法でテストを書いている。テストを動かす所まで漕ぎ着けるのが重要と考えているためである。 また、ジュニアメンバーが多い環境では「変数にクエリをつければ要素が取得できる」ことが伝わりやすい。少しでもトリッキーな実装が来るとテストをメンテナンスできない状態になりやすいため、高度な知識なしでできる方法を採用している。

デストラクチャリングを使ってクエリ対象のAPIだけ取り出す方法

次のようにrender関数内から値を取り出す際、分割代入(デストラクチャリング1を使う方法がある。 クエリに使うAPIを分割代入で取り出しておくことで、次の行では直接アクセスできる。

必要なAPIだけを使ってクエリする必要がないため、テストの記載が簡潔になる。

it("h1ヘッダーテキストが存在すること", () => {
  // デストラクチャリングを使いrender関数で取得したオブジェクトからgetByTextメソッドを取得する
  const { getByText } = render(<About />);
  // 取得してあるのでgetByTextが直接使える
  expect(getByText("About")).toBeInTheDocument();
});

テスト内で複数要素が必要だと、都度render関数を呼び出すことになる。テストが簡潔に書ける、という利点が失われるためcontentのように変数に取り出す方が良い。また、要素がイベント発火によって状態を変えたことをテストする場合、古い参照を見ているせいでテストが失敗することがある。

余計な切り分けが発生すると焦ってしまうため、可能な限り失敗しにくいように初期描画のコードを書いた方が良い。Testing Libraryのドキュメントでも、render関数にはコンポーネントを引数に渡している。

単純かつコンテンツへのアクセスが一回で済む場合、分割代入でテストを書くと良い。今回の例ではAboutというテキストにアクセスしているだけなので、分割代入が最適なように感じる。

screen APIを使ってレンダリングする方法

screen2 APIを使うとDOMの状態に直接アクセスできる。変数にrender関数の結果を取り出さなくてもテストが書けるので、取りかかりやすい。また、Testing Libraryの公式ドキュメントではscreenを使う想定でサンプルが書かれている。 使い初めはこの方法が取りかかりやすいかもしれない。

// あらかじめscreen APIをインポートしておく
import { render, screen } from "@testing-library/react";

it("h1ヘッダーテキストが存在すること", () => {
  // render関数を使ってAboutコンポーネントをレンダリング
  render(<About />);
  // screen APIを使ってDOMにアクセスし、テキストが存在するか確認
  expect(screen.getByText("About")).toBeInTheDocument();
});

注意点としては、screenはグローバル変数的にDOMにアクセスする可能性がある点。screenはグローバルな状態、つまり同じテストファイル内でのscreenを利用すると全て同じコンポーネントの状態を参照する。

つまり、途中で操作をして戻し忘れると後続のテストに影響を及ぼす

例えば、英語ロケールに変えた状態で次のテストを追記するとき、意図していないのにずっと英語ロケールのコンポーネントを参照してしまう。

it('ロケールを英語にする', async () => {
  const event = userEvent.setup();
  render(<About />);
  // ロケールを英語にする
  const button = screen.getByRole('button', { name: "ロケール変更" });
  // これ以降はずっと英語ロケールの状態でscreenがレンダリングされている
  userEvent.click(button);

  // テキストが存在していることを確かめる
  expect(screen.getByText("Type Your Name")).toBeInTheDocument();
});

it('名前を入力する欄のタイトルテキストが存在すること', () => {
  render(<About />);
  // 英語ロケール状態でテストが始まるため、並び順次第で意図通り動作しない
  expect(screen.getByText("名前を入力してください")).toBeInTheDocument();
});

これを防ぐには、状態を変えるテストは最後の方で実施すれば良い。しかし考えるのは大変だし見落としてしまう。プロダクション用コードでは基本的にscreenを利用せず、Testing Libraryの利用に慣れるため・どうしても必要なとき以外は避けた方が良い。

変数のスコープを短く切ってミスしないように工夫した方が良いよ、というのと同じである。

悪いフィードバック耐性をつけないとひよこエンジニアから抜け出せない

何かを上手くなりたいときはフィードバックを早くもらえるような習慣を身につけると良い。

プログラミング初学者が陥りがちなこと

最初から完成形を作ろうとしてしまう。完成形には次のものが含まれる。

  1. 実装後にどのように動けば良いか(仕様)
  2. どのようにプログラムを作るか(設計)
  3. どのように動作を確かめるか(テスト)
  4. プログラムを書くには何が必要か(タスクリスト)

4つを同時に考えてしまうので頭が混乱する。混乱すると手が止まってしまう。すると時間ばかり経って焦り、さらに頭が混乱する…という具合で状況が悪化する。

悪循環にハマる例

また、「自分の考えたことが予想通りいきそうか」を確かめるタイミングが遅い。具体的にはプログラムを書いてから動かす・テストコードを実行するまでの時間が長い。

結果、着手・実装・質問しに行くのを決める・質問をするまで全てに時間がかかる。進捗が悪いと周囲からあれこれ言われてしまう。焦ってしまうので思考がうまくできず更に状況が悪くなる。

なぜ完成系を作ろうとしてしまうのか

悪いフィードバックに対する耐性が低いため。具体的には実装どおりに動かないことや、自分の意図通りに進められないなどが当てはまる。テストの失敗やエラーの出力も怖がってしまう。

悪いフィードバック耐性が低いと、最初から完成系を作りたい気持ちが働いてしまう。結果、何をどう作るのかを実装の詳細と一緒に考えてしまう。全ての問題を一度に解決しようとするが、能力が不足しているためできない。それが悪いフィードバックになるため「完成系に近づけないと」という焦りが生まれてしまう。

対策

初学者かつ悪いフィードバック耐性が低い兆候が見られる場合、最初はペアワークをしてした方が良いと感じる。ペアワークでは、次の内容に一緒に取り組む。

  • 何ができれば完成か?のゴールを明確にする
  • ゴールを確かめるためのTODOリストを立てる
  • TODOリストを元にTDD(テスト駆動開発)的に進める

可能な限りTDDで進めるのがポイントだと感じる。TDDはテストを失敗させてから成功する進め方をする。初学者に取ってはテストの失敗は悪いフィードバックである。わざとテストを落としてから実装することで、「実装していないんだからテストが通らないよね」という風に認識を変えてもらう。

TDDで悪いフィードバック耐性をつけると、普段の仕事でも慌てなくなることが多い。意図通りの悪いフィードバックを作り出すのがポイントなのだと感じる。

また、TDDはTODOリストを元に進めて行くやり方をとる。TDDで進めることで、何をするか?とどう実装するか?を分けるようになる。実装の段階では実装だけに集中できるので、早く終わる。

試したけど上手く行かなかった対策

最初はリモートでできないか?と思いリモートでペアワークした。しかし、改善されなかった。リモートでは「悪いフィードバックを受けても平気だよね」という態度を示すのが難しい。また、トレーニー側が焦っているかも読み取りづらい。操作を認識するのにタイムラグがあるためTDDで進めることを強制するのも難しい。なので対面でないと難しいと感じた。

また、リモートで作業するとき時間を決めて必ず提出させるようにしたが、これも効果がなかった。締め切りがあればとにかく前に進めるだろう、結果フィードバックを受けにくるだろうと考えた。

実際は、ずっと手が止まってしまい「終わりません」とだけ言ってくることが繰り返されていた。止まっていた分の経験量が減るため時間がかかる割に身につかないことが続いた。

所感

初学者のOJTはトレーナー側がリモートワークできない。さらにペアワークをすると自分の時間が奪われたように感じる。初学者が考えるのを待つ必要があり、ペアワークの時間は長めに取らないといけない。

長期的に見れば初学者の状態を早く抜け出してもらえると分担できる仕事が増える。分担できるのでトレーナーも楽になる。

ただ、体感と異なるため超絶な忍耐力が必要となる。OJT特別賞与が欲しい気分になる1


  1. そんなものはない