KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

新規プロダクトにおけるFlutterのローコードツール利用とそのリファクタリング戦略

概要

こんにちは。PocketMusubiチームの南光です。

今回は、ローコードツールを利用してアプリを開発した際に工夫した内容を記事としてまとめました。 ローコードツールの開発知見記事はあまりみない気がするので、興味を持っている方やこれから利用を検討している方の参考になれば幸いです。

ローコードツールとは

ローコードツールの定義が曖昧な方も少なくないのではと思うので、一応wikiの抜粋を記載しておきます。

ローコード開発プラットフォーム (英: low-code development platform, LCDP) (ローコード(英: LowCode)とも言う)は、従来の手書きのコンピュータ・プログラミングの代わりに、グラフィカル・ユーザ・インタフェースと設定を通じてアプリケーション・ソフトウェアを作成するために使用される開発環境を提供するソフトウェアである。ローコードモデルにより、様々な経験レベルの開発者が、モデル駆動型ロジックと組み合わせたビジュアル・ユーザ・インタフェースを使用してアプリケーションを作成することが可能になる。このようなプラットフォームでは、完全に動作するアプリケーションを作成することもできるし、特定の状況のために追加のコーディングが必要となる場合もある。ローコード開発プラットフォームは、従来の手作業によるコーディングの量を減らし、ビジネス・アプリケーションの提供を加速する。

記載があるように、ローコードツールとはGUI操作でのアプリ開発環境を提供するツールであり、ビジネス・アプリケーションの提供を加速させることができます。

PocketMusubiにおけるローコードツールの利用の背景

まず簡単に当該プロジェクトの説明をすると、NativeアプリでMVP(Minimum Viable Product)の新規プロダクトを作るというもので、ベーシックな機能をベーシックに最速で提供することを目指したものでした。

また要望として、医療系でユーザの幅が広いのでiOS/Android双方にプロダクトを提供したいというものがあり、社内にはiOS/Androidそれぞれ一人しかエンジニアがいないという圧倒的に開発リソースが足らない状況でプロジェクトを進めていく必要がありました。

こういった状況であったため、当初からハイブリッドアプリやクロスプラットホーム技術の活用などを検討することとなり、最終的には以前Google I/O 2021で知ったFlutterFlowというFlutterのローコードツールの利用を検討することにしました

FlutterFlowとは

FlutterFlow is a low-code builder for developing native mobile applications. You can use our simple drag and drop interface to build your app 10x faster than traditional development.

FlutterFlowの諸々について書き始めると本筋からずれてしまうので、別の記事でまとめたいと思います。

PocketMusubiにおけるローコードツールの利用

FlutterFlowはコードをダウンロードできるツールだったので、ツールに委ねる部分と手動実装で作業するものを切り分けて作業を進めました。

具体的にローコードツールを利用した部分については下記のような感じです。

  • プロジェクトの作成
  • 各画面の作成
  • デザインの当て込み

ロジック実装はツール上でも可能だったのですが、ツール上だけでは対応が難しい実装もあったり設計観点での懸念もあったので、上記の対応のみに留めました。

またデザインに関しては、ツール上での対応が難しいものや、GUI操作の学習コストの方が実装の手間より大きくなってしまうようなものはコード上で対応しました。

ローコードツールを利用することの懸念とその対応

ローコードツールで作る部分は、ツールがコードを生成するので当然開発者が持つ品質担保の観点は含められません。 前述したようにロジックは自前で書いていくので問題ありませんが、コンポーネント部分はローコードツールに委ねる部分が大きく、コード品質は懸念として残っていました。

具体の1例としては、利用を検討していたFlutterFlowでは、Flutterの標準コンポーネントを利用するのではなく、FlutterFlowXXXのような独自のコンポーネントを定義しそれらを腐敗防止層として利用するような実装を行っている箇所が多くありました。 本来であればプラットホーム側のコンポーネントでインターフェイスの更新があった際にも追従できるよう、標準コンポーネントを利用して実装したいところです。

イベント向けのアプリなど単発のメンテナンス不要のプロダクトではこれでも問題ありませんが、継続的にメンテナンスしていく可能性のあるプロダクトでは、最初から技術的負債を抱えることになります。

最初から技術的負債を抱えるとなると利用に躊躇する部分もあると思います。 ただ技術的負債を抱えることが完全に悪なのかは、チームやプロダクトの状況によって判断が分かれる部分なのかなと思います。 というのも技術的負債というのは、どれだけ考慮しても外的要因などで起こり得るものだからです。

とくに新規プロダクトの場合は、ビジネス観点での変更により仕様変更する可能性や、プロダクトの成長にしたがって再設計が必要になる場面があります。 このような場合、最初のフェーズから作り込みをしてもコードを破棄することとなり設計に割いた時間が無駄になってしまいます。

こういった状況に適用できる考えとして、リファクタリング本を著したMartin Fowlerが提唱している犠牲的アーキテクチャというものがあります。

この考えの中では、実装をリプレースすることを念頭において設計していくことが重要であると述べられています。

犠牲的アーキテクチャとは,チームが現在開発しているものが,数年後には破棄しなければならなくなる(そうなってほしい)という事実を,現時点で受け入れるという意味だ。その時のリプレースが容易になるように今から考慮しておく,それが犠牲的アーキテクチャだ,と氏は言う。

犠牲的アーキテクチャは概念的なもので、具体の実装が述べられているわけではありません。*1 そのため、この考えに従いどこまでの技術的負債が許容できるかは、開発者が設計の過程で考えるべき点です。

自分の判断軸は本来の意味での負債の考え方と同じで、返せる見込みがあれば負債を背負っても許容できるが、逆に返せる見込みがない負債なのであれば最初から回避すべき技術的負債であると考えています。

当該プロジェクトの場合はどうなのかというと、ローコードツールの利用によって、画面側の実装に負債を残すこととなりましたが、システム的には下流の部分で影響範囲は狭いので負債を返していく算段はつけられると判断しました。

技術的負債をどう返していくか

次にどう技術的負債返済の道筋を立てているのかを記載します。 この記事の中では具体的にどうリファクタリングを行う土台を作れるように実装していったのかという点について記載し、体制など開発以外の内容は記事の本筋から外れるので割愛します。

Wikipediaやリファクタリング本の中で、リファクタリングを進めるにあたっては、プログラムの外観を変更しないようにテストを書いておくことが重要になると言及されています。というのもテストを行わずにリファクタリングを行ってしまうと、プログラムの動作が気付かないうちに変わってしまい、その原因を突き止めることが難しくなるためです。

これはリファクタリングを行うときのHowをまとめたレガシーコード改善ガイドにも同様の旨は記載されており、むしろ設計云々ではなく、技術的負債の定義*2はテストコードがないコードであるというような形で言及されています。

テストのないコードは悪いコードである。どれだけうまく書かれているかは関係ない。どれだけ美しいか、オブジェクト指向か、きちんとカプセル化されているかは関係ない。...

リファクタリングを行うにはテストが必要であるという考えはローコードツールを利用した際にも同様であるため、まずはテストコードをしっかり書いてリファクタリングに備えることで、技術的負債返済フェーズに備えています。

余談的な言及にはなりますが、リファクタリング本の中でリファクタリングをするタイミングについての言及があり、その1つに理解を深めるタイミングにリファクタリングを行うというものがあります。

新規プロジェクトのように、後からメンバーが増えていくことが予期されるプロジェクトでは、いかにドメイン知識を伝播させるかの準備をしておくかというのも大事になるため、そういう意味でもプロジェクトの初期段階からリファクタリングしやすい状況を整えておくというのは立ち上げ期の開発者が取り組むべき大事なタスクであると思っています。

テスト方針

当該プロダクトにおいてはユニットテストと、Visual Regression Testの2つを入れることにしました。

E2Eテストに関しては、当該プロジェクトは新規プロダクトであり頻繁に仕様が変わりうるフェーズであるため、メンテコストと恩恵のコスパを勘案した結果今は実装していません。

VisualRegressionTest

VisualRegressionTestingとは、画像を比較して差分を検出する回帰テストです。

VisualRegressionTestがあることで、リファクタリングなどを目的に下位のコンポーネントをリプレイスした際にもバグに気づくことができます。

当該プロジェクトではコンポーネントのリプレースを検討しているので必須と判断しており、すべての画面にテストが実装されています。

ユニットテスト

前述のようにロジックは、ローコードツールではなく通常の開発と同じように開発者が書いています。 そのためユニットテストの実装は通常の開発と同じように進めることができます。

単純にロジックにテストが書けているので安心という以外にも、ローコードツールはその都合上SDKのバージョンなどが古いことが多いので、SDKのバージョンアップをしたくなった際などにもある程度安心感を持って実行できます。

テスト対象

当該プロジェクトではカバレッジを高めることを重視しているわけではなく、テスト対象を選別して必要なところにテストが書けているかを重視しています。テスト対象を選別している理由としては、新規プロダクトであるため前述のように作り捨てをする機能があることを念頭に置き、早すぎるコードの抽象化や過剰な作り込みを避けたいためです。

認証など一般的に重要度が高いと考えられる処理以外でテスト対象の洗いだしを行う際には、バグが起きやすいところを見つけるため以下のような観点で判断しています。

  • 仕様が複雑なロジックを持つ
  • 変更頻度が高い

仕様が複雑なロジックを持つ

理由としては、複雑な仕様であるため考慮漏れが発生しやすいからです。

仕様が複雑なロジックを持つコードの判断には、Dependencies diagramを利用してあたりをつけています。 FlutterでのDependencies diagramの生成には、lakosというツールの使用をしています。 通常の指定だとかなり見にくい状態で吐き出されてしまうので、今はnode metricsの情報を主に確認しています。

lakos -o example.dot -m --node-metrics lib

こんな感じ。各項目の説明はdocで確認できます。

https://user-images.githubusercontent.com/11131753/167535115-11ed7f9e-17bf-4159-8682-50a05e914eb4.png

変更頻度が高い

理由としては、変更頻度が高いことでバグが入りこむ機会が多くなるからです。

変更頻度が高いコードの洗い出しにはgit logを利用してあたりをつけています。 具体的には下記のようなコマンドを実行し、変更頻度が高いファイルを降順で表示し確認しています。

git log --name-only --oneline | grep -v ' ' | sort | uniq -c | sort -nr

以上のような方法でテスト対象のコードを洗い出しています。

ここで洗い出した対象は、バグが起きやすいコードとしてそのままリファクタリング対象に転用することもできます。

補足

Flutterはカバレッジを生成できるコマンドを持っており、テスト対象を洗い出す際に活用できます。

ちなみに下記は現時点でのカバレッジを出したものです。

実装としては、全画面にVisualRegressionTestを入れたのと、重要度の高いロジックをHumbleObjectから切り離してテストを書いただけで、テストを書くためにローコード側で生成されたコードを大きく書き換えたりはしていません。 それでもカバレッジ40%くらいにはできているので、ローコードツールだからテストを書けないということはないんだろうと思っています。

https://user-images.githubusercontent.com/11131753/168929388-66f84fdb-09fa-4789-bf22-17409e1eb1cd.png

最後に

新規プロダクトをローコードツールを利用して開発した際に工夫した内容を記事としてまとめました。

今回はじめてローコードツールで開発してみて、やはりGUIでの開発は圧倒的にコスト(人的/時間的)を減らすかつ初速を出して開発ができるなと感じました。ただその一方、すべての作業をそういったツールでやろうとするとツールの学習コストが高くなっていくので、何をローコードツール上でやって、何をやらないか決めておくと良さそうに感じました。

課題として感じていたコードの品質面に関しては、やはり普通に開発者が実装していくのに比べて技術的負債と感じる部分は増えると思います。ただそれだけを原因に前述のような恩恵のあるローコード利用を躊躇するのではなく、起こりうる技術的負債に対して、許容できる技術的負債なのか、回避すべき技術的負債なのかフラットに判断して技術選定できれば良いのかなと思いました。

あと大事なのがローコードツールを使っていても、通常のプロダクト開発と同じようにテストをしっかり書いていれば安心だし、逆にローコードツールを使っていなくても、テストがなく品質保証ができない状態であれば不安定になるのかなと思っています。 そのため、新規プロダクト開発などでどれだけ人的/時間的リソースがなくても、テストへの意識は忘れず開発していきたいなと改めて感じました。

こういったローコードツール/ノーコードツールを利用して、継続的なプロダクト運用を念頭に置いた開発をしている会社の知見はあまり見ない気もするので、今後も学びや気付きなどがあれば記事にできればと思っています。

*1:具体の一例を記した書籍で進化的アーキテクチャという本もあります。https://www.oreilly.co.jp/books/9784873118567/

*2:正確にいうとレガシーコードという表現で言及されている