Toyokumo Tech Blog

トヨクモ株式会社の開発本部のブログです。

大学休学 => ゲーム業界でインターン => トヨクモに就職しました

初めまして、開発本部の松尾です。

22年度新卒として、4月にトヨクモに入社しました。

今回は、ゲームプログラマを志して情報系の大学に進学してから、大学を休学し、何故この業界に就職するに至ったのか、経緯を振り返りつつ、新入社員としての初心を書き残して置きたいと思います。

私の進路は比較的右往左往している方だと思いますので、こういう選択もあるのか、などと面白がって読んでいただければ幸いです。

高校生時代

高校生の頃からプログラミングをやっていて、朧げながら、自分はこの業界に進むのだろう、という意識がありました。

プログラミングを始めたきっかけはマインクラフトを始めとするゲームの改造であった為、転じてコンピューターによるエンターテインメント全般に興味が湧き、大学ではメディアプログラミングなどをやりたいと思い、一浪の末情報系の大学に進学しました。

浪人生時代

先程は一行で流しましたが、これまでの経緯を語る上で浪人生時代は欠かせません。 親に顔向け出来たものではありませんが、結論から言うと、この一年私はとにかく自分の好きなものを作るためにプログラミングをしていました。 (ゲームエンジンでゲームを作成したり、当時流行りだしたVTuberを作ってみたり)

是非は今のところ判断しかねますが、この一年が私の今後の人生を大きく変えたと思います。

この一年で2つのことを覚えました

  1. 手を動かすと身につく
  2. 人から教わらなくても、意外と大抵なんでも出来る(当時の主観)

大学生時代 〜前編〜

浪人生時代のある種の放蕩生活は自然な結果に帰結し、第一志望に落ちましたが幸い後期試験で他の国立大学の情報系に進学することが叶いました。

当初は大学生活を謳歌する気に満ちていたので、面白い人や活動に触れられたら良いなと思い、関心がある技術系を中心に数多くのサークルに所属しました。 実際、今でも関わりがあるくらいには、友人関係に恵まれ、これ以上無いという程に大学生活は楽しいものでした。

しかし、一年の後期には、自分の中で大学の単位を取るというモチベーションがほぼ無くなっていたのです。

恐らく謳歌しすぎてしまったのでしょう。下手に技術力の蓄えが(年齢にしては)あったし、当時は音楽など様々な創作にも手を出しており、「割と何でも出来る人」として扱われているうちに、自分でもそう錯覚するようになっていました。

実際、当時の関心の中心は、学術的な知識が必要にならないという意味で、応用的な分野が多かった為か、作りたいものの作り方が思いつかない、という場面に遭遇することはありませんでした。

そして、その分野に於いては、授業や参考書で体系的に知識を得ようとするよりも、実際にハンズオンで学習することが、少なくとも自分にとっては最も効率の良い学習方法である、ということを肌で感じていました。

そうしているうちに、「今自分がやりたいこと」を実現するためには、大学の学位を取るということが、果たしてどれ程重要なことなのだろう、と考えるようになっていました。

大学生時代 〜後編〜

応用的な技術に触れているうちに、自分が手を出したくても出せない領域があることに気づきました。 例えば、当時作っていたモーションキャプチャシステムを小型化したいと考えたときに、今まではノートパソコンを背負ったりしていたものをマイコンに移行したいと考えました。しかし、今までのようにスムーズに行きません。

そこにはOSやドライバという概念があり、今まで目を向けていなかった世界が露出していました。

今まで自分が使っていたツールは、Unityという「ゲームエンジン」で、C#という言語が、Monoという「実行環境」で動いており、デバイスの情報を取得出来るのは、SDKの提供者が、より低レイヤーにアクセスするための「バインディング」を提供しているに過ぎない、ということを悟ったのです。

とにかくこの時点で、自分が使っているツールに対する理解の重要性を思い知りました。そして、いつの間にか私の関心は、なぜコンピューターが動いているのか、3Dモデルを表示出来るのはなぜか?などの、「仕組み」に寄っていったのでした。

そのうち、今まで作品を実現するための手段であった技術が、純粋に関心の対象となりました。(愛を込めて「技術バカ」と呼称しています。)

インターン時代

すっかり技術バカになった私が、改心して大学で真面目にコンピューターサイエンスを学ぶかと思いきや時既に遅し。

尖った人間で有りたい、という自己実現の願望があったのでしょう。

大学の勉強のモチベーションを持ち直すのも難しかったのもあり、

「ここまで来たからには、持ち前の意欲を生かして、社会で通用するレベルの技術を知ろう」

と考え、振り切れることを選びました。大学を休学し、一年間フルタイムでインターンをすることにしたのです。

働かせて頂いたのは、「Cysharp」というCygamesの子会社で、多いと数千単位でスターがついている、有名なOSSを数多く公開している会社でした。

主に開発に携わっていたのは自社製品ですが、ライブラリ製作者の視点で、ときに専門的な知識を活用したりすることもありました。なにより、それを実際に世界レベルで実践している人が近くにいることは、とても新鮮で刺激的な体験でした。

また、新規のOSSライブラリの開発に関わらせて頂いたことも貴重な体験であり、誇らしくもありました。

そして、思わぬ気づきもありました。

  • 技術に長けた人であっても、会社での判断に於いては、「技術バカ」に一歩引いた態度を取っている。
  • コミュニケーション能力は重要

私が「優秀な技術者」として捉えていた方は、CTOでもありましたが、CEOでもあり、確かに商業を成り立たせる為の視点を持っていました。

ここで自分の原点を顧みると、自分にとっての技術は作品を実現するための手段であったことを思い出しました。 それと同時に、「人生において、様々なフェーズで、自分の価値観や関心が変わることがあり、それは自分が思っているよりも高頻度に起こる」ということを実感しました。

それまでもよく話に聞いていたことではありますが、やはり実感が伴って初めて価値観に影響するものです。それまでは、その時々の価値観に従った生き方をしていましたが、近い未来に価値観が変わったり、視野が広がったりすることを前提に行動をしても良いかもしれない、と思うようになりました。

現在

現在、私は岐路に立っています。

会社に入社して、様々なバックグラウンドを持った方々がおり、中には全く別の、高卒=>社会人=>大学というルートを辿っている方もいます。 立派な学歴を持った方の正確な知識に圧倒されることもあるかもしれません。

自分に足りない能力がある。それは大学で得るはずのものだ。と感じたら、夜間に通ったり、大学に戻るということもあり得るでしょう。

価値観が変わっても無駄にならないことはあると思っています。それは成長することであり、今私がこの会社にいる理由の1つでもあります。

トヨクモは、開発のスピード感もあり、優秀な方々がいるので、成長の場としてもうってつけだと思います。 この初心を忘れず、今後の社会人生活も精進していく所存です。


トヨクモでは一緒に働いてくれる技術が好きなエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。

医学部を卒業後、トヨクモに就職して1年が経ちました

初めまして、開発本部の細木です。

21年度新卒でトヨクモに入社し1年が経ちました。

今回は就職から1年経って今までの経験などを振り返ってみたいと思います。

就職までの経緯

医師以外の道

高校の進路選択では、将来医師として地元で働きたいと思い、地方国公立大学の医学部に進学しました。 1浪の末合格し進学することはできましたが、その後大学時代を悶々と過ごすこととなりました。

医学の勉強が性に合わず、成績は低空飛行でした。 幸い良い友人に恵まれて何とか進級・卒業することができました。

人の役に立ちたいというモチベーションはありましたし、実習で患者さん達と話すのは楽しかったのですが、生涯これを生業としていくのは肌に合わないなという気持ちがありました。 また医師として働いている方々を見ていて、自分も彼ら・彼女らのようにやりがいを持って働いていけるのか?という不安が拭いきれず、5年生の頃から他の進路について模索し始めました。

ITエンジニアという職業

元々ITエンジニアの職業に対する漠然としたあこがれがありました。 その一方で、医学部を出てから企業へ就職すること自体一般的ではなく現実的ではないだろうなと考えていました。

実習をしていたある日、Lillianさんの転職エントリを目にしました。 この方は東大医学部を卒業し医師として働かれていましたが、心機一転してGoogleに入社されたそうです。 私はこの記事を読んで感銘を受けると共に、医学部卒でもITエンジニアとして雇ってもらうことは不可能ではないのかもしれないと思いました。 その時から就職について本格的に考え始めました。

内定を得るまで

基礎知識をつけるべく勉強を始めました。 Lillianさんのエントリの他、ネット上の記事を漁ってWeb開発の学習に良さそうだと感じたサービスを探し、取り組んでいきました。

Progate(5年生の10月頃〜)

まずは基礎の文法に慣れるためにProgateから始めました。 約1か月でPythonコース、JavaScriptコース、HTML/CSSコースを一通り完了しました。

何もわからない状態から始めましたが、手習いの意味でとても有用でした。 また、フロントエンドの知識は後々の学習の導入として役に立ちました。

AtCoder(5年生の11月頃〜)

基礎的なコードを書く能力がないとコーディング試験に通らないだろうなと考え、Progateの次に取り組みました。

約半年かけて茶色、約1年かけて緑色になれました。

後々のコーディング試験対策として役に立ったのはもちろん、SNS上で多くの人と交流でき、就職に関する情報やチャンスを得ることもできました。

また、はじめはPythonで問題を解いていましたが、マイナーな言語もやってみたくなり途中からはCommon Lispで解くようになりました。これがとても面白く、Lispを書くことに夢中になりました。 この頃からLispで仕事ができたら面白そうだな…と考えはじめました。

Web開発

成果物を作りたいと思い、Udemyで買った講座やネット上のチュートリアル等を進めました。 完成には至りませんでしたが、開発の雰囲気をつかむことができました。

就職活動

自社製品開発をしているスタートアップを中心に、馴染めそうな雰囲気の企業を応募先として考えていました。

応募先を探す中で、ClojureというLisp系の言語を開発に使っている企業があることを知り、それらの企業について調べていると、齊藤さんの退職エントリが目に止まりました。 このエントリを読んで、自分のような変わったバックグラウンドを持った人間でも受け入れてもらえそうだと感じ、応募してみることにしました。

国家試験が終わった翌日、弊社および候補として考えていた複数の企業に応募しました。 いくつか面接やコーディング試験を受けました。

最終的に弊社から内定をもらうことができました。 時期的に新卒は厳しいだろうな…と思っていたこともあり、非常に嬉しかったです。

入社後にやったこと

4〜5月

スケジューラーチームに配属

3日間の入社研修が終わり、早速スケジューラー開発チームに配属されました。

スケジューラーでは主にバックエンドをClojure、フロントエンドをClojureScriptで開発を行っています。 (モバイルアプリはTypeScript)

まずはスケジューラーのコードベースに慣れるため、フロントの易しめなタスクから始めることになりました。 ClojureScriptに加えて、Reagent (Reactラッパー) 、re-frame (Fluxアーキテクチャに基づく状態管理フレームワーク) などが利用されていて、慣れるまでに時間を要しました。 (1〜2ヶ月)

この時期はチームのスピード感に付いていくために必死だった記憶があります。 調べてもわからないことはチームメンバーにたくさん質問しました。

またCursive(IntelliJ)やGitなどの開発ツールを使い慣れていなかったためこれらについても勉強しました。

6〜9月

React Query導入

元々フロントの状態管理はre-frame中心でしたが、React HooksおよびReact Query中心に移行することになりました。 これらについて勉強しつつ、スケジューラに導入するタスクを行っていました。

バックエンド入門

フロントに慣れてきたので少しずつバックエンドを触り始めました。 次のようなことにチャレンジしました。

  • スケジューラーのAPIを構成するライブラリ (Ringstch.sqlなど) について一つずつドキュメントを読み、実際のコードベースに触れつつ学習する
  • SQLの入門書を読む (「SQL 第2版 ゼロからはじめるデータベース操作」)
  • 「テスト駆動開発」を読み、TDDを普段の開発に取り入れる
仕事の仕方について

タスクの進め方を考えたりハマった際の原因探索をしたりする工程に時間を掛けすぎてしまうことがありました。 進めたい内容についてメンターとの間で認識のずれがあると大きな手戻りが起きうるため、手を動かす前にきちんと認識合わせをするようにしました。

10〜11月のファーストリリースまで

この頃は11月のファーストリリースに向けて、残っている未実装機能やバグの対応を行っていました。 体感では一番忙しい時期でした。 (忙しすぎてあまり覚えていない)

11月ファーストリリース後〜3月

ファーストリリース後のバグ修正、機能追加を行っていました。 開発に慣れてきたこともあり、大きめのタスクを担当する機会が増えてきました。

Zoom連携機能

スケジューラーにはZoom連携機能があります。 予定を作成する際にボタン一つでミーティングルームを作成することができます。

予定追加画面からZoomミーティングを作成できる

この機能の公開に向けた修正、申請などを進めていました。 OAuthの理解に時間が掛かりました。 OAuthに詳しいメンバーに教えてもらったりドキュメントと格闘したりしていました。

予定詳細ポップアップの全画面表示

スケジューラーではカレンダー上で予定のカセットを開くとその予定の詳細な情報がポップアップで表示されます。 この表示を画面全体に広げられるようにしました。

予定詳細ポップアップを全画面化

機能要件をまとめ、叩き台実装を作るところから始めました。 開発のメンバーやデザインチームの方々と相談しつつ、UXや細かいレイアウト等を詰めていきました。

最近

大きめの機能実装やDBマイグレーションを伴うリスクの高いタスクが増えてきました。

応用情報技術者試験

CS関連の基礎知識を埋めたくなったので個人的に受験しました。

1年働いてみた感想

自己肯定感を得ることができた

これが就職して一番良かったと感じることです。

弊社で仕事を始めて、至らない部分がありながらも成果や態度が評価され、人間としての自己肯定感を得ることができました。 弊社では、レベルアップしていくために今自分がすべきことが具体的で、やったことの成果が評価としてすぐに返ってきます。 学生の頃と比較してかなり気が楽だと感じます。 (もちろん個人差があると思います)

仕事のスタイルが自分に合っている

普段の仕事では自分の席で黙々と作業する時間が大半です。作業中に割り込みが入ることも少ないです。

私はマルチタスクが苦手で、仕事を自分のペース・段取りで落ち着いて進めることができる今の環境は理想的だと感じています。

Clojureが楽しい

Clojureの良いと感じるところはたくさんありますが、特に以下の点が気に入っています。

  • 書き方で迷う要素が他言語と比較して少なく本質のロジックに集中できる
  • 構文がないに等しく、覚えていないといけないことが少ない
  • 原則immutableで脳に優しい
  • フロントとバックエンドで共通の言語を利用できるため、コンテキストスイッチが小さい
  • 先述の通り疎結合なライブラリが多いため、学習コストが小さく抑えられる
  • 明示的であることが好まれるため、ライブラリの暗黙的な挙動に振り回されることが少ない
  • Structural Editingが便利

非情報系出身でもやっていける

キャッチアップは大変でしたが、なんとか慣れることができました。

勉強すること、必要に応じてコミュニケーションを取ることができる人であれば問題ないと思います。

周りが優秀

新卒、中途を問わず優秀な人ばかりで、周りから日々多くのことを学ばせてもらっています。 特に情報系出身の人・開発経験の長い人などは、自身と比較してかなり地力の差があると感じます。

いずれ追いつけるように努力していきたいですね…

競プロにかなり助けられている

上記の通り、就職活動のため・および趣味で競技プログラミングの問題を解いていました。

開発経験に乏しい非情報系の私が、短い時間で最低限のコーディング力を身に付けて就職できたのは競プロのおかげだったと思っています。

業務で直接的にアルゴリズムの知識を使うことは多くありませんが、言語を使いこなすための基礎的な感覚を身に付ける上で非常に有用だったと感じます。

普通に新卒就活をするならまずWebの勉強をちゃんとするべきだと思いますが、競プロも無駄にはならないと思います。

まとめ

私は医師の道から一転してITの世界に入りましたが、現在もトヨクモで元気に日々を過ごしています。

就職して1年経った今でも、職を得て生活できていること自体奇跡なのでは?と思うことがあります。

今までの縁に感謝しつつ、今後も成長していければと思っています。


トヨクモでは一緒に働いてくれる技術が好きなエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。

OSSへの寄付活動について

開発本部の飯塚です。

弊社ではオープンソースソフトウェア(以下、OSS)に対して継続的な寄付という形で開発の支援を行っています。

この件について今まで特にアウトプットがなかったので、これを機にトヨクモがOSSをどう思っているのか、なぜ寄付をしているのかをアウトプットしてみたいと思います。

トヨクモの思い

OSSへの思い

弊社では製品を開発する中で様々なOSSを利用しています。 その中には企業で作られたものもあれば個人で作られたものも当然あります。

それらOSSは弊社製品を形作る重要なピースです。

欠けてしまうと代替の有無に関わらず製品の品質を保つための改修が必須となってしまいます。 つまり利用しているOSSが継続して開発され安心して利用できる状態に保たれていることは、弊社製品開発において品質の担保だけでなく開発効率の向上にも繋がっていると考えています。

寄付への思い

企業はともかく個人で開発されているOSSは、開発にかけられる時間やモチベーションの問題もあり常に継続して開発できるとは限りません。 個人の趣味や興味で作ったOSSは基本的にはお金にはならず、限られた時間の中で無償で開発し続けられる人はごく一部です。 ましてや有名になればなるほどバグ報告への対応などで疲弊する要素ばかり増え、開発をやめてしまうケースも多々あるかと思います。

前述の通りOSSは弊社製品を形作る重要なピースです。 OSS開発者へ貢献に対する感謝を示し、また開発に対するモチベーションの維持などに役立ててもらえたらと願い、弊社では金銭的な寄付を継続して行っています。

寄付状況

現時点(2021/08/27)では Clojure のOSSを主として以下に対して毎月の寄付を行っています。

Thomas Heller氏

Thomas Heller氏は shadow-cljs の開発者です。 github.com

shadow-cljs provides everything you need to compile your ClojureScript code with a focus on simplicity and ease of use.

shadow-cljs は ClojureScript のコードを JavaScript にコンパイルするためのビルドツールで、npm モジュールをシームレスに利用できたり、開発時に必要なREPLの提供などを特徴としています。

Clojure で開発している弊社製品のフロントエンドにおいてはもれなく shadow-cljs を利用しています。 一時期は figwheel-main を使っている製品もありましたが、npm モジュールの利用し易さや開発時の体験の良さから shadow-cljs に移行しています。

今では効率的なフロントエンド開発に無くてはならない存在であるため、継続的な開発を支援するために毎月の寄付を行っています。

Clojurists Together

Clojurists Together とは Clojure コミュニティにおいて定期的に選出されたOSSプロジェクトに対して資金援助し開発のサポートをしている団体です。

援助するための資金は企業や個人からなる有志の寄付から集められており、そこに対して毎月の寄付を行っています。

www.clojuriststogether.org

そのため特定個人への寄付とは異なり、Clojure コミュニティにおける OSS 開発全体を支援している形になります。

前述の通り弊社では Clojure を開発言語とした製品がいくつかあり、 Clojure コミュニティの発展は製品開発の効率や製品の品質に直結します。 よって Clojurists Together への寄付は大変意義深いものと考えています。

今後の予定

今時点での寄付状況は以上ですが、まだまだ足りていないと感じています。

実際に弊社製品で利用しているOSSだけでなく、弊社として今後有望と思われるOSSも対象として寄付による支援を行っていきたいという考えもあり、 今は対象としたいOSSを検討している段階です。

繰り返しにはなりますが、OSSの継続的な開発は弊社における製品開発としても重要な要素の1つであるので、 そこに対して弊社としても利用者の立場として継続的な支援をこれからも行いたいと思っています。


トヨクモでは一緒に働いてくれるエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。

トヨクモ株式会社を退職します

開発本部の齊藤です。2021年の1月で退職することになりました。業務内容の紹介も兼ねて今まで何をやってきたか、これから何をするかについて書きます。

入社までの経緯

私はもともと大学の学部を卒業した後に大学院の修士課程に進学する予定でしたが、卒論があまりうまく行きませんでした。なんとか先生に介護してもらいながら卒論を終えたものの、自分で思うような卒論が書けず、 「大学院に進学しても地獄のような思いをするだけでは?」という思いが強くなっていきました。今から振り返ると、結局はあまり興味がない分野(バイオインフォ)を選んでしまったことが卒論の失敗の根本的な原因だったと思います。

大学院進学をあきらめた私は、とりあえず生活のためにどこかに就職しようと思いました。とはいっても何をすればいいかわかりませんでしたし、すでに2月になっていました。 ひとまず、すでに会社員をやっている知り合いに連絡を取って助言をもらえないかと考えました。 そこで、以前SICP勉強会で面識のあった北川さんに連絡をとることにしました。すると、「バイトから社員のコースに載せられるかも知れないので、とりあえずバイトをしましょう」という話になり、2月からアルバイトをすることになりました。

アルバイトでは、Vue.jsやClojureの勉強をしてのんびり過ごしていました。製品には1行だけコミットした記憶があります。FormBridge のボタンが消えるみたいなバグの修正だったと思います。 「4月から9月まで契約社員で採用します」という話だったので、是非お願いしますと言って入社しました。契約社員とはいえ、福利厚生などの待遇面では賞与以外の違いはなかったと記憶しています。家賃補助や、タイミングが良かったためストックオプションも貰えました。

入社してからやったこと

kintone-query-builder (2019年4月)

kintone-query-builder-phpというクエリビルダを作って社内製品でも利用するようにしました。PHPを書くのは初めてだったのですが、PHPStormが助けてくれたので、ひとまず完成できました。 詳細についてはブログ記事にも書きました。

kintone REST API向けのPHPのクエリビルダを作りました - Toyokumo Tech Blog

pc-auto-save (2019年5月)

pc-auto-save.jsというプリントクリエイターの機能を拡張してくれるJavaScriptのメンテナンスを行いました(現在はプリントクリエイターの標準機能として取り込まれたため、このJSは存在していません)。 このJavaScriptはClojureScript製で、私が「Clojureやりてぇ〜」と訴えたらこの仕事にアサインしてくれました。ちょっとした不具合の修正が目標だったのですが、結局Reagent(Reactのラッパー)で 書き直しました。Clojureの良い練習になりましたし、REPL駆動開発やStructural Editingに慣れることができました。SICP勉強会でだいぶSchemeには慣れていたのですが、原始的な開発環境で開発していたため、 Clojureの素晴らしい開発環境、ツールに感動しました。

この時期にOn Lisp勉強会を開き始めたのもいい思い出です。結局コロナなどもあって、現在は凍結しているのですが...。 私はあんまり人に話しかけるのが得意ではないので、勉強会を通して他の社員と交流できたのが良かったなと思いました。

kMailer (2019年6月~10月)

6月からはClojure製品の1つであるkMailerの仕事をしました。ブラウザのdevtoolなどを使ってボトルネックを見つけて修正したり、大量のデータでの挙動を改善するような仕事を経験できたので勉強になりました。一定以上のデータを使うときにconcatで爆発するというのがあり、遅延評価は難しいなと思いました。 データマイグレーションなどの慎重さを求められる仕事もあったのですが、開発本部の先輩が仕事の進め方や技術的な知識をアドバイスしてくれたおかげでなんとかうまく行きました。 また、インフラをElastic Beanstalkに移行するという仕事も経験することができました。EBは便利ですね。

9月には正社員に登用されることになり、給料も上がって嬉しかったです。

新製品DataCollect (2019年11月~2020年5月)

11月からは新製品DataCollect開発にアサインされました。DataCollectでは、エクセルのような式を書いて、kintoneのデータを利用して計算をすることができるのですが、その「エクセルライクな式を処理して計算をする」モジュールを僕が作成することになりました。 ちょっとした外部DSL的なものです。

;; 目標は以下のような関数compileを作ること
(compile エクセルライクな式の文字列)
;; => 以下のようなmapが返ってくる
{
 :compiled-fn (fn [input] ...) ;; inputを与えると実際の計算をする
 :how-to-build-input ... ;; compiled-fnに与えるinputを作り上げるために必要な情報
 }

かねてから「(プログラミング)言語処理系とか興味あるんですよね〜」と言っていたので、興味や適正を加味してのアサインでした。 開発は私と開発本部長の木下さんの2人でしたが、私にとっては少人数でも初めてのチーム開発でした。 仕様を無から作り上げていくという初めての体験ができましたし、チーム開発を通して技術的なコミュニケーションのスキルが上がったと思っています。疑似コードで議論するのは楽しかったです。 3月にリリースしてからは、ユーザの利用できる関数を追加したり、配列 + 1のような計算間違いをコンパイル時に検出してユーザに提示する、簡易型チェックの仕組みを作ったりしました。

技術的には「たぶん作れるだろうな〜」と思っていたのですが、実際に製品に組み込むとなるとやはり不安になるものです。一通り必要な要件を満たすものを作成できて自信になりました。

リリース後は私がDataCollectを一通り見る役目として、バグ修正や機能追加、コードレビューをやりました。 トヨクモ株式会社は製品がとても多いため、1人1製品くらいの状況になるので、責任感と緊張感を味わえたのは良い経験だったと思います。

別の新製品開発 (2020年6月~)

2020年の6月からは仕事の中心が新製品開発が中心になりました。「最速で製品をリリースする」のが当初の目標だったため、 ひとまずDataCollectのリポジトリをクローンしてその上に新規機能を実装しキメラのようなアプリを作り、その上で不要な機能を削除するという開発方針で進めていました。 開発本部の方針として「製品の差別化につながらない部分にコストを割かないようにする」というのがあるためです。

DataCollectの開発では、Webインターフェースやバッチ処理の部分はほとんど木下さんが作ったため、ClojureのWeb開発はほとんどやってませんでした。 新製品開発はしばらく私一人での開発だったため、全部やる必要があり、ClojureのWebアプリケーション開発にだいぶ慣れることができたと思います。

また、新製品開発のコツなどもアドバイスしてもらい、勉強になりました。

  • やることの全体像を(スプレッドシートなどで)可視化する
    • モチベーションの維持に役立つ
    • やることを明確にできる
    • 他人に何をどこまでやっているのか共有できる
  • UIのモックから作る
  • 非機能要件は、機能要件を固定してからテストを書いた上で取り組む

結局、リリースすることができませんでしたが、現在も開発中です。

会社員生活の感想

  • 知り合い(北川さん)が社内にいたため、割とすぐ馴染めたと思います。知り合い経由で就職するとその点楽だなと思いました。
  • 私は自分の意見が通らないと面白くないなと思ってしまう人間なので、妥当性があれば誰の意見でも採用してくれる雰囲気は私にあっているなと思いました。また、人数が少ないため、その分一人ひとりの裁量が大きくなっているのもフィットしていたかなと思いました。自分の貢献が明確なのでやっていて楽しかったです。
  • 新製品開発ばっかりやっていたような気がします。会社の方針として、「1つの製品に機能を大量に盛るのではなく、シンプルな製品を多くリリースする」というのがあり、新製品開発に関わりたい人には良い環境だと思います。
  • 規模の大きい製品で、パフォーマンスなどが問題になる部分を担当する機会がなかったのはもったいなかったかなと思いました。安否確認サービスという製品があるので、体験できるチャンスはあったと思うのですが、私は一切関わりませんでした。
  • Clojure(LISP)で仕事をできる環境は日本ではとても限られているので、「Clojureで仕事する」というレアな体験ができてよかったです。実際ほとんどClojureしか書いてませんでした。
  • 給料がドンドン上がっていくのが面白かったです。正社員になったタイミング、2019年12月、2020年6月、2020年12月のタイミングで上昇し、最終的には契約社員時代と比べて約月給が1.5倍になりました。なぜ月給ベースでの上昇割合を書いたのかと言うと、2年未満しか勤めていないため実年収のサンプルが1つしかないですし、昇給のタイミングが1年に複数あったため誤解が生まれない書き方がわからなかったためです。トヨクモ社の2020年度まで年収モデルとして、月給x14がベースの年収として考えられるため、実際の年収に換算しても1.5倍であってると考えられます。
  • 会社の上場を経験できました。といっても、未だに上場の意味などもよく分かっておらず、会社に胡蝶蘭が来て「へぇ〜」と思ったくらいの感想しかありません。私のストックオプションも行使権を得る前の退社により破棄されるので関係ないですし...。
  • 大学でいろいろあり、入社時にはノイローゼのような状態になってしまっていましたが、一定の収入があり、人間らしい生活ができると自然と回復していくものなんだなと思いました。特に2020年になってからはだいぶ元気を取り戻した感じがありました。貯金残高を眺めて精神状態を回復させるのはよく使っていました。
  • slackのemojiを追加して遊ぶのは楽しかったです。自分が追加したemojiを他の社員が使ってくれると嬉しくて積極的に追加していました(一部「治安が悪い」という理由で撤去されたものもあります)。
  • 朝起きて夜寝る生活ができるようになりました。

なぜ退職するのか

退社後は大学院修士課程に進学して項書換え系 の研究をする予定です。定理証明支援系やプログラミング言語の基礎に興味があるので、研究は楽しめるのではないかと考えています。

もともと研究職に対する漠然とした憧れがあり、いつかは挑戦してみたいなとはおもっていたのですが、暇な時間を使って勉強しているうちに早く挑戦してみたい気持ちが強くなったので、このタイミングで挑戦することにしました。

木下さんとの退社の面談でも「ストックオプションの行使権の取得まで待って挑戦する選択肢もあるのでは?」というアドバイスも受けたのですが、新しいことをやるには勢いが大事だと思っているので、熱意が冷めないうちに退職したほうが良いだろうなと判断しました。やりたいことを我慢しながら別のことをするのは精神的に耐えきれない人間なので、爆発する前に決断できて良かったと自分では思っています。

また、会社員をやりながら大学院に進学したりする選択肢もあるとは思うのですが、私の最終的な目標としては研究者としての職を得ることなので、大量に時間を確保する必要があると思ったので候補から外れました。

退職を心に決めたのは2020年の5月でしたが、それ以来モチベーションを維持するのが難しくなってしまい、とてもしんどかったです。加えて 、退職が近づくにつれドンドンやる気がなくなっていきました。100%のパフォーマンスが出せていない状態が長く続いてしまい、申し訳無さを感じています。

研究をやるにしろ、会社員的なスキルは大事だと思われるので、会社員をやったことは悪くなかったかなと思います。また、ソフトウェア開発のスキルはしばらく需要が高いと考えられるので、挫折してもまた再就職すればいいやーというセーフティネットがあると思うと、学部時代のような漠然とした不安感に苛まれることもなく、前向きに生活できるのではないかと期待しています。博士号取れるようにとりあえず5年間は頑張りたいと思います。実はまだ入試に受かってすらないのですが...。

ありがたいことに「暇になったらアルバイトしてくれ」と言われているので、また仕事するかもしれないです。


トヨクモでは一緒に働いてくれる技術が好きなエンジニアを募集しております。

採用に関する情報(https://scrapbox.io/toyokumo-recruit/)を公開しております。 気になった方はこちらから(https://toyokumo.co.jp/jobs/)ご応募ください。

メール送信ライブラリ tarayo のすすめ

開発本部の飯塚です。

今回は担当している製品で利用しているメール送信ライブラリを自社製のものに移行した経緯や利点などをまとめてみました。

概要

  • kMailer というサービスの開発/運用を行っている
  • コア機能であるメール送信のライブラリとして postal を使っていたが問題点がいくつかあった
  • その問題点を解決しているライブラリが他になかったので開発し、移行した

kMailer とは?

kMailer ロゴ

kMailerkintone 内に登録されているデータを利用してメール送信するというサービスです。

コア機能であるメール送信をする方法としては現状、以下2つの方法を提供しています。

  • お客様のSMTPサーバーを利用したメール送信
  • Google との OAuth 連携を利用したメール送信

今回は前者の「お客様のSMTPサーバーを利用したメール送信」で利用しているライブラリの移行の話です。

postal とは?

postal は Clojure でSMTPによるメール送信をしようとした場合の デファクトスタンダードと言っても過言ではないライブラリです。 SMTP の他に sendmail を使ったメール送信にも対応しています。

以下、テスト用メールサーバーを使ってメール送信をする簡単な例です。

(require '[postal.core :as postal])

(postal/send-message {:host "localhost" :port 1025}
                     {:from "alice@example.com"
                      :to "bob@example.com"
                      :subject "hello"
                      :body "postal world"})

postal の問題点

postal の API はとてもシンプルで postal.core/send-message が提供されているのみです。 この関数に接続先のサーバー情報とメールの内容を渡せばそれだけでメール送信をしてくれます。 とても簡単で良いのですがいくつか問題がありました。

  1. 開発が止まっている JavaMail に依存している
    • JavaMail は ver 1.6.2 のリリースを最後に Eclipse Enterprise for Java の一部となり Jakarta Mail と名前が変わり、こちらで開発が続けられています。
    • JavaMail には宛先として不正なメールアドレス(例えば bob@example.com. のように末尾がドットで終わる)を渡しただけでも NullPointerException を投げる問題があり、後継の Jakarta Mail を使いたいという要望がありました。
      • Jakarta Mail の場合 javax.mail.internet.AddressException 例外が Domain ends with dot というメッセージと共に投げられるのでとても親切です。
  2. 一度に複数のメールを送った場合のエラー時の挙動
    • 複数のメールを送信する場合、単純に doseq でループを回しているだけなので、途中で前述のような NullPointerException が投げられてしまうとそれ以降のメール送信は止まってしまいます。
      • その上、リクエスト処理の結果は doseq 内で捨てられてしまっているため、どこまで送信リクエストが完了したのかもわかりません。
  3. メールサーバーとの接続/切断が暗黙的
    • 複数のメールを一度に送信する場合でメール単位の結果を取得しようとすると、メール単位で postal.core/send-message を呼び出すしかないのですが、メールサーバーとの接続/切断は関数内で閉じられてしまっています
      • そのため、例えば Gmail (smtp.gmail.com) を使ったメールの一斉送信をしようとすると、メールサーバーへの接続/切断を繰り返しすぎて Too many login attempts エラーが簡単に発生してしまいます。

後者2つの問題解決のためのプルリクエストは出したもののマージには至らなかったので、kMailer では postal をフォークして改修したバージョンを製品に使うという中途半端な状態が続いていました。

tarayo とは?

tarayo は postal での問題点を解決するために弊社が開発した SMTP クライアントライブラリです。 名前の由来は郵便局の木として知られるタラヨウからです。

postal との主な違いは以下の通りです。

  • Jakarta Mail ベース
  • SMTP のみをサポート (単一機能のみを提供)
  • メールサーバーとのコネクションが明示的

以下、postal でのメール送信例を tarayo を使って書き換えたものです。

tarayo ではメールサーバーへの接続(tarayo.core/connect)とメール送信(tarayo.core/send!)を別々の関数として提供しているので、 postal ほど簡単ではありません。(with-open しているだけなので十分簡単ではありますが)

(require '[tarayo.core :as tarayo])

(with-open [conn (tarayo/connect {:host "localhost" :port 1025})]
  (tarayo/send! conn {:from "alice@example.com"
                      :to "bob@example.com"
                      :subject "hello"
                      :body "tarayo world"}))

tarayo でどう postal の問題点を解決しているか

では postal にあった問題点を tarayo ではどうやって解決しているかを簡単にまとめてみました。

  1. 開発が止まっている JavaMail に依存している
    • tarayo は最初から Jakarta Mail ベースで開発しています。
  2. 一度に複数のメールを送った場合のエラー時の挙動
    • tarayo.core/send! は1通のメール送信だけを扱う関数としています。
      • そのため複数のメールを一度に送る場合はライブラリの利用者側でループする必要はあります。
      • 1通の送信だけが対象なので、結果は勿論捨てずに返しており、送信対象と結果の紐付けも容易です。
  3. メールサーバーとの接続/切断が暗黙的
    • メールサーバーへの接続をするだけの関数として tarayo.core/connect を提供しています。
      • これにより with-open マクロなどを使って明示的に接続を切る必要は出てきますが、切断するタイミングを任意に決められるので一斉送信などをする場合により柔軟な対応が可能になっています。

kMailer で利用する上での問題点の解決を第一に開発したライブラリではありますが、なかなか使い勝手の良い出来になっているかと思います。

最後に

kMailer ではすでに postal から tarayo への移行が完了しています。 平日1日で数万通のメールを送っていて、これらがすべて tarayo でカバーされるメールです。

接続先のメールサーバーはお客様の設定次第であり多岐にわたるため、postal から tarayo への移行後に接続できなくなるメールサーバーが出ないかの不安はありました。 しかし今のところそういった問題は見つかっておらず健気にメール送信処理を頑張ってくれています!

このようにある程度の規模での利用実績も積めたので、もし Clojure でメール送信をする機会がありましたら一度 tarayo を試してみていただければなと思います。


トヨクモでは一緒に働いてくれるエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。

AWS CLI を Babashka スクリプトでラップして楽しよう

開発本部の飯塚です。

業務で Babashka を使う機会を無理矢理作ってみたので、その副産物を公開してみます。

動機

  • CloudWatch Logs からログを抽出して調査したい
  • AWS CLI を使ってもいいが、クエリの発行、クエリの完了待ち、結果取得が面倒
  • 良い感じにラップしたスクリプトを用意して誰でも簡単に使えるようにしたい
  • Clojure で!!

Babashka

Babashka@borkdude 氏が開発している Clojure のコードを スクリプト として高速に実行できる環境です。 高速さの背景には GraalVM を利用したネイティブイメージ化があります。 これにより JVM 言語で度々話題にあげられるスタートアップタイムがほぼ無視できるくらいの速度で起動してくれるのが大きな特徴です。

$ time clojure -e '(+ 1 2 3)'
6
clojure -e '(+ 1 2 3)'  1.47s user 0.10s system 195% cpu 0.806 total

$ time bb '(+ 1 2 3)'
6
bb '(+ 1 2 3)'  0.00s user 0.01s system 23% cpu 0.070 total

今回はこの Babashka を使って AWS CLI をラップしたスクリプトを作ってみました。

前提

この記事で紹介しているコードは使い方は以下が済んでいることを前提としています。

  • AWS CLI がインストールされていること
  • aws configure で鍵情報などが設定済みであること

作ったもの

コードが少し長かったので Gist に置いてあります。

AWS CLI wrapping script by Babashka

先頭で require している cheshire.core は Babashka にバンドルされているもので、何もしなくてもデフォルトで使うことができます。 他に何がデフォルトで使えるのかなどは README の Usage に書かれています。

基本的には aws.logs.core/query を使えば AWS CLI を通じてログを取得できるようにしています。 取得するログの範囲は指定した日付の 00:00:00 から翌日の 00:00:00 としています。

もし aws.logs.core/query では自動化されすぎてやりたいことができないという場合は、 aws.logs.core/query から呼んでいる関数群を時前で制御すれば大抵のことはできるように関数を分割しているつもりです。

使い方

まずこれから紹介するコード類は以下のようなディレクトリ構造になっていることを前提とします。

- run.sh
- src
   |
   +- aws
   |   |
   |   +- logs
   |       |
   |       +- core.clj
   +- example
       |
       +- core.clj

最初に aws.logs.core を呼び出すコードです。 以下ではログのメッセージに "NullPointerException" が含まれるものだけを抽出してみています。 フィルタリングなどのクエリについては CloudWatch Logs Insights クエリ構文 を参照してください。

(ns example.core
  (:require
   [aws.logs.core :as logs]))

(defn -main [& args]
  (when (< (count args) 3)
    (System/exit 1))

  (try
    (let [[yyyy mm dd & [region]] args
          filter-query  "filter (@message like \"NullPointerException\")"]
      (doseq [result (logs/query {:group-name "/aws/path/to/your.log"
                                  ;; aws configure しているリージョンとは違うリージョンにしたい場合は指定可能
                                  :region region
                                  :yyyy yyyy :mm mm :dd dd
                                  :queries [filter-query]})]
        (println result))
      (System/exit 0))
    (catch clojure.lang.ExceptionInfo ex
      (println (.getMessage ex) (ex-data ex))
      (System/exit 1))))

次に実行するためのスクリプトです。 無くても実行できますが用意しておくとクラスパスを都度指定する必要がなくなり便利です。

#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)
bb --classpath ${SCRIPT_DIR}/src -m example.core -- $@

あとは以下のように年月日を指定して実行すればログを取得して出力してくれます。 今回の例では取得したログをそのまま出力してしまっていますが、必要に応じて欲しい情報だけピックアップして出力しても良いでしょう(業務で使っているスクリプトではそうしてます)

./run.sh 2112 9 3

最後に

ここで紹介したスクリプトは Clojure を使うことを除けば他の言語では大して目新しくないものでしょう。 このスクリプトの良いところは Clojure で書けるというだけでなく、REPL 駆動で開発できるということが個人的にはかなり大きいです。 取得したログから必要な情報だけピックアップするにしても、REPL 駆動であれば一時的に結果を適当な var に束縛しておいてあれこれ試すことは容易です。

そうして作ったスクリプトがスタートアップタイムを気にせずに実行できるので開発体験としてはとても良いものがあると思います。

もし REPL 駆動での開発に興味があれば Vim で始める Clojure 開発 も併せて参照してください。


トヨクモでは一緒に働いてくれるエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。

Vim で始める Clojure 開発

Vim/Neovim 向けに Clojure の開発環境を提供するプラグイン vim-iced を使って Clojure 開発環境の構築をしてみましょう。 なお vim-iced は個人的に開発している拙作の Vim プラグインで、実際に私が業務で(勝手に)利用しているという実績があります。

前提

  • Vim もしくは Neovim がインストールされていること
    • Vim は 8.1.0614 以降、Neovim は 0.4.0 以降が必要であることにご注意ください
  • Java がインストールされていること
  • macOS もしくは Linux 環境(本記事では Ubuntu を想定)であること
    • Windows 環境は vim-iced 自体がサポートしていないので、VM などで Linux 環境を用意してください

ゴール

本記事の内容を実践したことで以下の状態になっていることをゴールとします。

  • Vim を使った Clojure の開発環境が構築でき、REPL 駆動開発ならびにテスト駆動開発が体験できていること

やること

  • Clojure 実行環境のインストール
  • vim-iced のセットアップ
  • サンプルプロジェクトの作成
  • REPL 駆動開発のお試し

Clojure 実行環境のインストール

Clojure の実行環境は複数ありますが、今回は多くのプロジェクトで使われている Leiningen (ライニンゲン)を使った手順を紹介します。

なにはともあれインストールから始めましょう。

# macOS の場合
brew install leiningen

# Linux の場合
curl -fLo lein https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
chmod +x lein
sudo mv lein /usr/local/bin   # パスが通っているディレクトリであればどこでも可

インストールができたら簡単に Clojure のコードを実行して試してみましょう。

  • lein repl コマンドを実行して REPL(Read Eval Print Loop) を起動します。
    • 初回は起動に必要なライブラリ類をダウンロードするので時間がかかります。 user=> が出てくるまでしばしお待ちください。
  • user=> が出てきたら Read の状態なので、何かコードを書いて評価(Eval)してみましょう。結果が表示(Print)されるはずです。
    • E.g. (+ 1 2 3 4 5) を評価すると 15 が表示されます。
  • 確認ができたら Ctrl + d を押すか、(exit) を評価して REPL を閉じましょう。

vim-iced のセットアップ

vim-iced は Vim プラグインとして提供しています。そのためプラグインマネージャーを利用していない場合はまずその導入から始めましょう。 すでにプラグインマネージャーを利用している場合は次のセクションは飛ばして構いません。 なお本記事では vim-plug を使った例を紹介します。

vim-plug のインストール

Vim/Neovim で設定ファイルのパスが異なるので vim-plug の配置先も異なることにご注意ください。

# Vim の場合
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

# Neovim の場合
curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

各種プラグインのインストール

Vim/Neovim の設定ファイルを用意します。 Vim の場合は ~/.vimrc、Neovim の場合は ~/.config/nvim/init.vim です。 すでに設定ファイルがある場合は「追加する設定」の部分だけの追加で問題ありません。

# Neovim の場合は先にディレクトリを作成しておく
# mkdir -p ~/.config/nvim

cat <<EOT > ~/.vimrc
set nocompatible
set encoding=utf-8
scriptencoding utf-8
filetype plugin indent on

" ==== 追加する設定 ここから ====
" プラグインのインストール先ディレクトリは必要に応じて変更してください
call plug#begin('~/.vim/plugged')
" 次のうちいずれかが必要
Plug 'ctrlpvim/ctrlp.vim'
"Plug 'junegunn/fzf'
"Plug 'liuchengxu/vim-clap'

" 必須
Plug 'guns/vim-sexp',    {'for': 'clojure'}
Plug 'liquidz/vim-iced', {'for': 'clojure'}
call plug#end()

" vim-iced でのデフォルトキーマップを有効化
let g:iced_enable_default_key_mappings = v:true

" 任意) 見やすさのためスクリーンキャプチャ内では有効にしています
set splitright
let g:iced#buffer#stdout#mods = 'vertical'
let g:iced#buffer#error#height = 5
" ==== 追加する設定 ここまで ====
EOT

上記内容の設定ファイルが作成できたら、Vim/Neovim を起動している場合、一度再起動して :PlugInstall コマンドを実行してください。 もしくはターミナル上から vim -c PlugInstall -c qa を実行するのでも可です。

上記の例だと ~/.vim/plugged 配下に各種プラグインが配置されたことが確認できます。

ls ~/.vim/plugged

iced コマンドにパスを通す

最後に vim-iced が提供している iced コマンドへのパスを通す必要があります。 vim-iced のインストール先が ~/.vim/plugged である場合は以下のコマンドでパスを通してください。

export PATH=$PATH:~/.vim/plugged/vim-iced/bin

なお iced コマンドはインストールディレクトリ配下のファイルを参照するので、すでにパスが通っているところへ iced コマンドをコピーしても正しく動作しませんのでご注意ください。 以下を実行して何かしらバージョン番号が表示されれば正しく動作しています。

iced version

サンプルプロジェクトの作成

ここまでで Vim で Clojure 開発を始められる準備は整いました。 Leiningen を使ってサンプルプロジェクトを作って実際に動かしてみましょう。

lein new hello-iced
cd hello-iced

サンプルプロジェクトのディレクトリ構成を簡単に説明すると以下の通りですが、今回使うのはソースコードのみです。

Dir/File Description
src ソースコードはこちら
test テストコードはこちら
project.clj プロジェクト名、バージョン番号、依存ライブラリなどを記述する設定ファイル

REPL 駆動開発への入り口

まずはソースコードを開きます。Leiningen で作成されたプロジェクトでは プロジェクト名.core という名前空間が用意され、名前空間のドット区切りがそのままディレクトリ/ファイル名に反映されます。 唯一の例外がハイフン(-)で、これはディレクトリ/ファイル名上ではアンダーバー(_)に変換されます。

vim src/hello_iced/core.clj

開いたファイルは以下のようになっているかと思います。

(ns hello-iced.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

REPL への接続

接続とIcedEvalの実行例

ソースコードを開いたらおもむろに :IcedJackIn コマンドを実行してみてください。 iced コマンドにパスが通っていれば、ステータスラインに OK: Leiningen project is detected のようなメッセージが表示され、しばらくすると Connected. が表示されます。 この Connected が出た状態が Vim の裏側で起動している REPL に vim-iced が接続している状態です。

※ 2022/05/27追記 ここから

:IcedJackIn は Vim 内で REPL のプロセスを起動しますが、別途 REPL を起動しておいてそちらに接続する方法もあります。 プロジェクト配下で iced repl コマンドを実行してみてください。 すると lein repl 同様に REPL が起動するので、Vim 上で :IcedConnect コマンドを実行すると別途起動した REPL に接続できます。 こちらの方法だと Vim を終了しても REPL は起動したままなので、REPL を再度立ち上げる必要がないというメリットもあります。

※ 2022/05/27追記 ここまで

試しに :IcedEval (+ 1 2 3) コマンドを実行してみてください。Vim 上で 6 という結果が表示されることが確認できるはずです。

コードを評価してみる

ソース上のコード評価の実行例

次にソースの末尾に (comment (foo "iced")) を追記し、その括弧の中にカーソルを移動して、ノーマルモードで <Leader>et (:IcedEvalOuterTopList)とタイプしてみてください。

これはカーソル配下のトップレベルのフォームを対象に評価することを意味しています。 なお <Leader> はデフォルトでバックスラッシュ(\) なので \et となります。

なおここで comment フォームを使っているのは、名前空間 hello-iced.core が読み込まれたときに無駄に評価されないようにするためです。 vim-iced の <Leader>et ではデフォルトで comment フォーム内のコードを評価するようになっているので、既存のコードを評価して試してみたいときには comment フォームを使うと便利です。

(ns hello-iced.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

(comment (foo #_カーソルはここ "iced"))

結果はどうでしょうか?恐らく期待とは違い nil が表示されたかと思います。 vim-iced では関数の戻り値を Popup ならびにステータスラインに表示して、標準出力は別の場所に表示するようにしています。 ノーマルモードで <Leader>ss (:IcedStdoutBufferOpen) とタイプすると標準出力の表示先(以下、StdoutBuffer)が別ウインドウで表示され、期待した結果が出力されていることが確認できると思います。

変更内容を即座に確認する

ソースを修正しその内容を即座に確認する実行例

ではサンプルプロジェクトの foo 関数で println の引数を以下のように変更してみましょう。 この変更で StdoutBuffer に出力される内容が期待したものに変更されるかを確認したいと思います。

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "is awesome!!!")) ;; 出力内容を修正

変更できましたか? できたら foo 関数の中にカーソルを移動して <Leader>etfoo 関数を再評価してください。 REPL 駆動開発では起動している REPL 内に評価された結果が保持されて、それを使って開発を進めていきます。 なので単にコードを変更しただけでは REPL 内の foo は変更前のままなので再度評価してあげる必要があります。

再評価できたら末尾の comment フォームを改めて <Leader>et で評価してみてください。 StdoutBuffer に変更後の内容が出力されることが確認できるかと思います。

このようにフォーム単位で簡単・迅速に挙動が確認できるのがREPL駆動開発の強みです。

上記の例では関数単位ですが、さらに言うと例えば (def x "bar") を評価しておいて、 println フォーム内で <Leader>ee (参考: Evaluation ranges)で評価すると関数内のフォーム単位でも動作確認可能です。

テスト駆動開発への入り口

次にテスト駆動開発に触れてみましょう。 ここでは整数のリストを渡して、その合計値を返すという簡単な関数を題材としてみます。

まずは core.clj に以下の関数を書いて評価してください。 この時点では合計を算出する処理はまだ書かずにとりあえず 0 を返すだけです。

(defn sum [ls]
  0)

テストの作成

テストの実行例

では hello-iced.core/sum のテストを書いてみましょう。 テストコードはサンプルプロジェクト直下の test ディレクトリ配下にあります。

ファイルを探して開いても良いのですが、ここでは :IcedCycleSrcAndTest コマンドを実行してみましょう。 これによりソースファイルに対応するテストファイルとして test/hello_iced/core_test.clj を自動的に開くことができます。(ファイルが実在しなくても名前空間からパスを推測して新規作成も可能です)

開いたファイルは以下のようになっているかと思います。

(ns hello-iced.core-test
  (:require [clojure.test :refer :all]
            [hello-iced.core :refer :all]))

(deftest a-test
  (testing "FIXME, I fail."
    (is (= 0 1))))

サンプルプロジェクトでは必ず失敗するテストしか書かれていないので、この a-test は以下のように書き換えてください。

(deftest sum-test
  (is (= 6 (sum [1 2 3])))
  (is (= 10 (sum [1 2 3 4]))))

ここでは hello-iced.core/sum[1 2 3] と、[1 2 3 4] というリストを渡して、 それぞれの結果が期待したものになるかをテストしています。 このフォーム上にカーソルを移動させてノーマルモードで <Leader>tt (:IcedTestUnderCursor)とタイプしてみてください。 これはカーソル配下のトップレベルのフォームを対象にテストを実行することを意味しています。

ステータスラインにテスト結果の概要が表示されると共に、別ウインドウで期待した結果と実際の戻り値が異なるエラーが2つ表示されることが確認できるかと思います。

ソースの修正

テストの再実行の例

ではテストが通るようにソースを修正していきましょう。 改めて :IcedCycleSrcAndTest コマンドを実行してソース・ファイルに戻りましょう。

まずはすこしズルをして以下のように sum を修正してみてください。

(defn sum [ls]
  6)

修正できたら <Leader>et で再度 sum を評価し、 テストも再実行します。 もう1度 :IcedCycleSrcAndTest で移動、 <Leader>tt をタイプとしても良いのですが、ちょっと面倒なので今度はソースコードを開いたままノーマルモードで <Leader>tr (:IcedTestRedo)とタイプしてみてください。

これは直前に失敗したテストを再実行することを意味しています。 すると今まで2つ出ていたエラーが1つになったかと思います。 あと1つのエラーが残っていますが、今度はズルをせずにきちんと書きましょう。

(defn sum [ls]
  (apply + ls))

再度評価してもう1度 <Leader>tr でテスト実行してみてください。 今まで別ウインドウで表示されていたエラーはウインドウと共に消えて、ステータスラインに成功した旨が表示されたかと思います。

ここまででテストの書き方、実行方法、そしてテスト駆動による修正方法がわかったかと思います。

リンター/フォーマッター の設定

※ 2022/06/01追記

REPL駆動以外にも開発時にあって欲しいのはリンターとフォーマッターのサポートです。 より具体的には以下の2点は開発において必須と言っても過言ではないかと思います。

  • clj-kondo による静的解析が常にかかる
  • cljstyle によるフォーマットがファイル保存時に行われる

clj-kondo による静的解析の設定

Vim においてリンターの結果を表示するためのプラグインはいくつかあり、デファクトスタンダードと言えるものはありません。

主要なプラグインに関しては clj-kondo の以下のドキュメントにまとまっているので、こちらを参考に設定すると良いでしょう。

github.com

なお筆者はこのドキュメントにおける coc.nvim の設定を採用しています。

cljstyle によるフォーマットの設定

フォーマッターについては vim-iced 自体がデフォルトで cljfmt を使ったフォーマットに対応しています。 しかしチーム内でコーディングスタイルを統一したい目的で cljfmt 以外を使いたいニーズもあり cljstyle を使ったフォーマットも勿論サポートしています。

https://liquidz.github.io/vim-iced/#formatting_customize

vim-iced でのフォーマッターの設定

vim-iced にてフォーマッターとして cljstyle を使いたい場合の設定は簡単で ~/.vimrc などに以下の1行を追加するのみです。

let g:iced_formatter = 'cljstyle'

もし cljstyle がインストールされていない場合は :IcedFormat のようなフォーマットに関するコマンドを実行することで 「iced コマンドにパスを通す」でパスを通したディレクトリ配下にインストールするかという案内が始まるので、それに従うと簡単にインストールすることが可能です。

保存時にフォーマットする設定

vim-iced が提供するフォーマット関連のコマンドは基本的に非同期でバッファ上のテキストを更新しますが、保存時は非同期にしてしまうとフォーマット結果が正しくファイルに保存されなくなってしまいます。 そのため vim-iced では同期的に動くフォーマットコマンドも用意しています。

これらのコマンドを BufWritePre に対する autocmd で使うことで保存時のフォーマットを実現できます。

以下ドキュメントからの抜粋ですが設定例です。

https://liquidz.github.io/vim-iced/#format_on_writing_files

aug VimIcedAutoFormatOnWriting
  au!
  au BufWritePre *.clj,*.cljs,*.cljc,*.edn execute ':IcedFormatSyncAll'
aug END

なお環境やフォーマット対象のコードによってはバッファ全体のフォーマットに時間がかかり Vim がフリーズする時間ができてしまうこともなきにしもあらずです。 その場合は :IcedFormatSyncAll の代わりに :IcedFormatSync コマンドを使うとカーソル配下のフォームだけを対象にフォーマット可能です。 フォームを編集 → 保存をこまめに行っている場合だとこちらの設定でフリーズはほぼ回避できますが、保存時のフォーマット対象がカーソル配下のフォームに限定されるのでフォーマット漏れが出る可能性があることに注意してください。

最後に

最初に掲げたゴールを振り返ってみましょう。

Vim を使った Clojure の開発環境が構築でき、REPL 駆動開発ならびにテスト駆動開発が体験できていること

簡単ではありましたがゴールの通りの体験ができたでしょうか? これをきっかけとして Clojure での開発に興味をもっていただけたなら幸いです。

なお vim-iced 自体にも興味を持っていただけたなら、 ここで紹介していない多くの便利な機能がまだあるので、ぜひドキュメントを読んでみていただければと思います。

Spacemacsを利用している方はこちらを参考にしてください。

tech.toyokumo.co.jp


トヨクモでは一緒に働いてくれるエンジニアを募集しております。

採用に関する情報を公開しております。 気になった方はこちらからご応募ください。