ジモティーで未経験からエンジニアを始めた話

はじめに

こんにちは、ジモティーでAndroid開発をしている林と申します。 エンジニア未経験でジモティーに入社し、もうすぐ2年近くが経とうとしています。 ここでは未経験で入社した者がどのようにプログラミングの実務をこなして、他チームと案件に携わっていったかのプロセスをご紹介できたらと思います。

※未経験の記事を書いていて大変恐縮ですが、ジモティーでは現在経験者の方優先で採用活動を行っております。

入社前のスキル

まず前社では主にセールスフォースを使ったデータ管理や資料作成、エクセルのマクロ作成などで少々プログラミングを行った程度でした。 マクロ作成からプログラミングに興味を覚え、その時Pythonがこれから来るらしい!という情報からPyQを使って独学で勉強し、Djangoを使ってWEBブログのサイトを作成しました。 しかし当時求人を見てみたらPythonの求人が少なく、Rubyなら可能性が広がると考えて、UdemyでRuby + Railsを勉強し、写真も投稿できる簡易型掲示板を作成しました。 これらをポートフォリオとしてジモティーを受けて、アプリエンジニアとして採用させてもらいました。 そしてここからまたアプリを一から勉強し、RetrofitとRXを使ったアプリを作成して一応仕組みは理解しているつもりで入社することになります。 しかし、この時はまだエンジニアの入り口にも立てていなかったのです。。

入社後の挫折

まずGitを使ったチーム開発もしたことのない初心者だったので、プルリクの作成方法やGitの基本的な使い方などから勉強が始まりました。 そしてhttpをhttpsに変更する簡単な修正を行い、「なんとかこれならできる!」という感触を最初は持っておりました。が、、すぐに全く歯が立たなくなりました。 まず、独学でやっていた時はクラスを分ける意識もなく、必要なものをただ上から書いていただけだったので、プロダクトコードを深く追って行ってもどこに何があるのか分からず、「そもそもどこでnewしてるんだよ!」という有様でした。 インターフェースまでたどり着いても、実体が別にあり、これ呼ばれてるのに何もないじゃん?とますます混乱が増大していきました。 また膨大なコードと初めて対峙したので、引数が多くあった時点で思考が停止してしまう有様でした。

そしてここからアーキテクチャー研修を受けることになります。

やっとエンジニア開発の入り口に到達

ジモティーのAndroidではApp層、Domain層、Infra層と層を分けており、MVP+ クリーンアーキテクチャーをもとに実装がされています。 アーキテクチャーを理解しないとまず改修や画面作成などは手をつけるのが難しいです。 そして先ほどのどこでnewしているかわからない問題やインターフェースと実体の関係性も、DaggerというDIの仕組みがわからないと全体が見えてきません。 アーキテクチャー研修によりこれらの全体像を掴み、「何を聞いたらいいのかも分からない状況で、とにかく分からないです、、」といった感じから、やっと開発ができる入り口に到達することができました。

※弊社のAndroidアーキテクチャー詳細についてはこちら

そしてただプログラミングに慣れる日々

ここから簡単な案件に携わることになり、ディレクターから要件を確認し、サーバーチームやiOSチームのメンバーと連携して開発を行っていきました。 最初のうちは簡単な案件でもチーム開発を理解するために、マネージャーに振り返りを行ってもらい、この疑問はこのタイミングで聞いておくとよかったとか、次からは自分で提案してみてもいいですね、などコミュニケーションスキルの向上なども相談に乗ってもらいました。 またエンジニアチームでは技術点を設けており、1ヶ月にこの点数獲得したら評価はA、A+など分かりやすい目標も持って開発を進めていきました。 仕事場では全く聞いたことのない単語や専門的な話が飛び交っていて、何だかすぐに全てを理解しなければならない錯覚に陥ってしまい不安が高まっていたのですが、ジモティーでは週に一回1on1の面談を設けていて、そこでマネージャーに今やるべきことや、「そのうちできるといいくらいなので大丈夫ですよ。やっていけばできるようになりますよ。」とメンタル面でもケアをしてもらいました。

転機

半年ほど経った時に初めて新規画面を一人で実装することになりました。1ヶ月くらいかかるプロジェクトをジモティーでは大玉案件と呼んでいますが、初めてそのような案件に参加して上記のアーキテクチャー研修でやった内容をプロダクトコードに反映させて、リリースも行いました。 結果的に障害もなく無事にリリースできたので、「これやっていけるんじゃないか。。」とエンジニアとして自信につながる貴重な経験を得ることができました。

そして今に至る

現在はプログラミング能力が向上したのか、ただコードを見慣れたからなのか定かではないですが、エンジニア開発も問題なくこなしており、広告担当者として責任の大きい案件の実装も行っております。 またiOSの研修も行ない、ジモティーではAndroidiOSで同じアーキテクチャーを採用しているため、どこに何が実装されているのかおおよそ予想がつくようになっており、スムーズに研修をこなすことができました。 ゆくゆくはiOSも同じように開発できるようになることを目標に日々精進しております。

最後に

入社当初は毎日会社にいさえすれば成長できるはずだと考えて日々邁進しておりました。 環境面においても、プログラミング技術においても、「慣れる」ことがいかに大事かを学んだ期間でもあったかと思います。

最後まで読んでいただきましてありがとうございました。 もしジモティーにご興味ありましたら、こちらでエンジニアの方を募集しております!

※現在は経験者の方優先で採用活動を行っております。

iOSのデザイン周りの開発と改善について🧸

f:id:jmty_tech:20210107095931p:plain:w300

ジモティーエンジニア紅一点の@chanNaruです🧸

iOSチームで開発を行っています。

軽い自己紹介

絵や漫画を描いたり車でドライブすることが好き、 個人でアプリを作ったりなども f:id:jmty_tech:20210107094156j:plain:w110f:id:jmty_tech:20210107094420p:plain:w110f:id:jmty_tech:20210107094448j:plain:w110f:id:jmty_tech:20210107094451j:plain:w110f:id:jmty_tech:20210107094454j:plain:w110 f:id:jmty_tech:20210107095812p:plain:w200

-> wantedly:iOSDCにてデザイン関連の登壇などをしたり、個人アプリの紹介など書いてます

-> Qiita:イラストあれば理解しやすいよねをモットーにたまーに書いてます(最近書いてない..)

話すテーマ 🌼 iOSのデザイン周りの開発について

好きなことが講じてデザイン周りにも興味があるということで 今回はまだまだ課題のあるiOSチームのデザイン周りの開発について、 具体的には私が入社してからの約2年間でどのような改善があったのかについて書こうと思います🧸

入社当時

もっとデザイナーやディレクターと連携できるような環境に身をおきたいとジモティー に転職をしたのですが、

「前職でできなかったデザイン周りの開発改善やるんだ!🔥」

だったり、

「(転職当時)今アツいAtomicDesign取り入れたいな〜〜〜」

という野望を持っていました。

実際入社した時には丁さんの記事でも書かれてましたが CleanArchitectureを導入するなど設計面の見直しが進められているなか、

  • デザイナーとの意思疎通がとりづらい
  • 画面とパーツが完全に依存している

などといったデザイン面の課題があり、

この課題の結果として

  • (1) 類似パーツを量産するコスト
    • 各パーツが画面依存しているので同じようなパーツを量産してしまっている
  • (2) 変更による1つ1つの改修コスト
    • デザイン変更によって類似しているパーツを1つ1つ修正しなければならない
  • (3) 既存と同じでとなった際のコスト
    • 「xx画面と同じ表示で!」などとなった時にその既存の作りを確認し真似しなければならない
  • (4) どこでどのようなパーツが使われているのか探すコスト
    • どんなパーツがどこでどのように使われているのかコードを探すのが大変

などといった負が誘発されることで非常に効率の悪い開発が行われていました。

f:id:jmty_tech:20210107095931p:plain:w500

f:id:jmty_tech:20210107095953p:plain:w600

↑定期的に行われる微妙な色が違うよ指摘..😭や、 f:id:jmty_tech:20210107100045p:plain:w300

↑ここの実装どうなってたっけ..😭などなど。


これらの課題を解決するために、以下の3つの開発見直しを行いました。

①cellの分解

特に改善したポイント: f:id:jmty_tech:20210107100611j:plain:w500

1つの画面のxibにパーツが全部組み込まれているため、その画面からしか使用できず他画面に同じ表示を追加する場合もそれを流用できないような作りになっていました。 その結果、何度も同じパーツを画面ごとに作るコストが発生。

(※ xib(≒storyboard):画面やパーツを作るときに作成するGUIのファイル)

これについては、

1画面=1xib(必要なパーツを全て持っている)
↓
1画面=1xib+α xib(適宜必要なパーツを参照する)

f:id:jmty_tech:20210107100633p:plain:w500

に作りを変えることで「画面とパーツが完全に依存している」状態を改善し、 毎回作り直さなければならないコストを減らしました。

(この課題の副作用として2019年のiOSDCの登壇でもネタにしたのですが、 特に投稿画面はパーツが多すぎることでxibを開いても何も表示できなかったりフリーズしたりなどという問題もあったので この辺りも開発効率改善にすごく寄与しました😭)

f:id:jmty_tech:20210107100736p:plain:w600

②cellの共通化

特に改善したポイント:

f:id:jmty_tech:20210107100754j:plain:w500

「①cellの分解」を行ったことで、「画面とパーツが完全に依存している」状態は改善できましたが さらにもっと汎用性がでるようにコードの改修を行いました。

具体的には以下のように、共通パーツを呼び出すためのprotocolを定義し各画面のテーブル用のリストを作る際に用いることで、 どの画面でも好きなように呼び出して扱えるcellにしました。

private var tableViewData: HogeTableViewData?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let tableViewData = self.tableViewData else {
        return UITableViewCell()
    }
    let cell = tableView.dequeueReusableCell(withIdentifier: tableViewData.get(indexPath.row).cellId,
                                                 for: indexPath)
    switch viewData {
        case let data as CommonCellViewDataPtcl:
            (cell as? CommonCell)?.setUp(viewData: data)
        default:
            break
    }
    return cell
}

*********************

struct HogeTableViewData {
    private var list: [HogeViewData]
    init(list: [HogeViewData]) { self.list = list }
}

protocol HogeViewData {
    var cellId: String { get }
    /**/
}

struct HogeViewData: HogeViewData, CommonCellViewDataPtcl {
    let cellId: String = R.nib.hogeCell.name
    let title: String = "たいとる"
    /**/
}

protocol CommonCellViewDataPtcl {
    var title: String { get }
    var backgroundColor: UIColor { get }
}

extension CommonCellViewDataPtcl {
    var backgroundColor: UIColor { return .white }
}

class CommonCell: UITableViewCell {
    private var viewData: HogeViewData?

    func setUp(viewData: CommonCellViewDataPtcl) { /**/ }
}

このようにすることで既存と同じようなデザイン指示があった際に、 使いたいcellのprotocolを呼び出すだけでパーツを流用できるようになりました。

変更があった際にもその1つのcellクラスを修正するだけで完了できますし、 また、上記ではbackgroundColorをextension化することで、「配置とかは同じだけど背景色が違う」などといった 「同じだけど微妙に違うから共通化しにくい」にも対応する形で汎用性をもたせています。

({ return .white }と初期値を設定しておくことで、意図しない変更にも強い作りになっています)

共通パーツについては、 類似パーツの量産を防ぎ、どんな共通パーツがあるのかを探しやすくするための工夫として、Zeplinを使ってカタログ化しています🧸 f:id:jmty_tech:20210107100818p:plain:w600

(※ Zeplin:デザインリソースから指示書やスタイルガイドなどを自動で生成するソフトウェアで俗に言う「デザイン共有ツール」。 ジモティーではこちらのパーツの一覧管理用ににのみ使用しています。 カンプなどはFigmaを使っています。)

導入タイミング・キッカケとしては、大きなprjで7画面ほど新しい画面を作ることになった際に毎回作るコストを減らすために考えられました。

このコードの課題としては、cellForRowAt内でviewDataPtclの分岐を忘れてしまうと、setupが呼ばれずエラーにならず空セルとなって表示されてしまう点があり、 ジモティーでは結合テストでその問題が起きないように担保しています。

データと画面を依存させずにこの問題をテスト以外で担保できないかなど含め、コードの精査を今後も続けていく予定です✊

③ラベル/ボタン/カラーレベルのコンポーネント

特に改善したポイント: f:id:jmty_tech:20210107100941j:plain:w500

ボタンやラベルでもデザイナーの意図しない色やサイズになっていたり、 既存を参考にしようにもどこでどう定義されているのか探すのが大変という課題に対して、 一番小さい単位のパーツ類をコンポーネントとして定義して各所で使うようにしました。

f:id:jmty_tech:20210107101000p:plain:w600

デザイナーチームではAtomicDesignという概念を既に使用していたので倣う形で以下のような資料をもとにデザイナーと打ち合わせを行ない、 そこそこ前に書いた記事なので現在と一部異なりますが、

【swift】コンポーネントを実装してパーツのスタイルを使いまわそう!😊【デザイン】

f:id:jmty_tech:20210107101030p:plain:w200

ここに書いている実装をプロジェクトに導入しました。

// ボタン:XLarge
@IBDesignable class XLargeButton: UIButton {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.layer.cornerRadius = 2.0
        self.layer.borderWidth = 1.0
        self.titleLabel?.font = UIFont.largeButtonTitle()
    }

    // 背景塗りつぶしパターン
    func primaryFill(title: String = "") {
        if title.isPresent {
            self.setTitle(title, for: .normal)
        }
        self.layer.borderColor = R.color.jmtyColor()!.cgColor
        self.backgroundColor = R.color.jmtyColor()
        self.setTitleColor(R.color.invertedTextColor(), for: .normal)
    }
}

※Colorについては最近コードとパレットの二重管理からxcassetsの一元管理化に変えています

このような定義を行うことでスタイル名の「共通言語」が生まれ、 カンプ共有ツール上でスタイルの指示をしてもらうだけでコンポーネントを流用しデザインに反映するという効率アップだったり、 どこでどのようにスタイルが使われているのかをプロジェクトから探しやすくなり、 修正工数もグンと削減することができました👏

f:id:jmty_tech:20210107101102p:plain:w600

もともとデザイナーチームでもエンジニアとのこれらに関する意思疎通がうまくできていないという課題があったため一緒に検討を進められたのはとても良かったです👍

まずはエンジニア側で現状の実装を元に仮組みで導入し、ある程度うまく実装にのった段階で本格的にデザイナーとルールやパーツ選定を行うという進め方をしたことで、 デザイナーと計画してから導入までの期間を短く出来、軌道に上手くのせることができたのではと思っています。

結果

上記の見直しを行ったことで、

  • 大凡UI周りの開発について工数の半分ほどを削減🎉
  • パーツの大量発生を防ぐことでプロジェクト自体のサイズの増大を防げる
  • 既存実装を探す手間を防ぐ

などの改善がありました。

終わりに

上記で書いたこともまだまだ導入を進めているところで、Objective-Cのコードもまだ残っていたり課題は他にも色々あります。

少しでも興味を持っていただいたり、こういうアイデアもあるのではという方がおりましたらお話ししたいです。

+.🧸ジモティーではエンジニアを募集しています🧸+.

インフラ構成とデプロイ事情

はじめに

はじめまして、サーバーサイド/インフラあたりを見ているエンジニアの佐藤です。

ここまでインフラ関連の話があまりなかったようなので弊社のインフラ構成とデプロイ事情についてご紹介します。

インフラ構成

まずはデプロイ事情の前にざっくりとしたインフラ構成からです。
弊社では主にAWSを利用しています。
簡単な構成は下記図の通りとなっています。

他のマネージドサービスやGCPも利用している部分はありますが、今回は割愛させていただきます。

f:id:jmty_tech:20201214160729p:plain

  • Web
    • サービスが稼働しているサーバで、NginxやRuby on Railsが動作しています。
    • AZ2つにそれぞれ用意しており、AutoScalingを用いて柔軟に台数を調整しています。
  • Batch
    • 定期実行が必要な処理が動作しています。
    • cronが現役で動いており、ジョブ数も多くなってきているのでジョブスケジューラの導入を検討したいところです。
  • Worker
    • 主にリアルタイム性の必要のない非同期処理だったり、push通知など大量のジョブをこちらで実行させています。
    • 最近AutoScaling対応しました。
  • MongoDB
    • メインで利用しているDBです。
    • MongoDB4.0からtransactionが導入されているので、ぜひバージョンアップして使ってみたいです。
  • RDS(Aurora Mysql)
    • 現状利用しているMongoDBのバージョンにtransactionがないため、課金周りなどのtransactionが必須となる処理のために利用しています。
  • Elasticsearch
    • 投稿リスト表示や検索処理周りで利用しています。
    • EC2上で立てて運用していますが、所々課題があるのでマネージドサービスのElasticsearchServiceを導入したいと思っています。

と、ざっくりとした紹介ではありますが、今回はこの構成の中で頻繁にデプロイが必要となるWebサーバのデプロイフローについても紹介していきます。

デプロイフローについて

弊社ではBlue/Greenデプロイを採用しています。 大まかな流れは図の通りになります。 f:id:jmty_tech:20201214160659p:plain

  1. Jenkins上からデプロイジョブを実行
  2. releaseブランチに対してコードのマージ命令
  3. マージされたコードでWeb Serverの準備開始
  4. 準備完了後BlueからGreenへ参照を切り替える
  5. 待機時間(※)終了後、起動されているWeb Serverを終了させてジョブが完了
    ※ 切り戻しに備えて一定時間待機させている

事前に各開発者が機能の実装を完了させ、GitHub上でコードレビューを行います。
動作確認等の最終チェックが済むとデプロイ可能になり、図の①を実施します。
あとは自動で順序通りに進んでいきフローが完遂されるという流れです。

切り戻しの際はデプロイジョブを停止させ、同じくJenkins上にある切り戻し用のジョブを実行します。
経過時間が30分未満の場合は、Blueが残っているため速やかに切り戻しが完了します。
それ以降は対象コードをリバートして通常デプロイで切り戻しします。

※ Batch/Workerについては、そこまで複雑ではないので割愛します。

影響が大きそうなリリース時に採用している手法

各種ミドルウェアのバージョンアップなど規模の大きな変更の際はカナリアリリースを採用しています。
直近ではRubyのバージョンアップ時に採用しました。
バージョンアップ済みのWebサーバを1台用意してELBへ接続し、1週間ほど動作させます。
問題なければ全Webサーバをバージョンアップ済みのものに切り替えます。
この手法を採用することで、最小限のリスクで本番環境での動作確認が可能になり大きな障害を回避できます。

また、まだシステム的にカナリアリリースの実施が確立できていないので、より実施しやすい仕組みを検討していければと思います。

改めて思ったこと

メンテンス性について

そこそこシンプルな構成になっているように思いますが、図の中で記載しているデプロイジョブが中々厄介者です。
Rubyで一連の処理が書かれているのですが、若干見通しが悪くメンテナンス性が悪いように感じています。

また、稀にJenkinsサーバがハングアップしていて、Jenkinsサーバ自体のメンテナンスも必要になっています。
マネージドに依存するのが良いのか悪いのか悩むところではありますが、AWSにはCodeDeployといったサービスもあるので、導入を検討していきたいと思っています。

デプロイ時間について

図の③で新しいWebサーバを用意しているのですが、ELBからの疎通確認完了までの時間が長く、1デプロイに6〜7分程度掛かっています。
うーん、長いですよね。
上記で記載の通り切り戻しの際、30分程度であれば速やかな切り戻しが可能ですが、それ以降だと通常デプロイをする必要があり多少の待ち時間が発生します。
遅れて障害が発生することもあるので、一定時間経過後でも速やかに切り戻しできるような仕組みがほしいところです。

参考までに他社の事例ではデプロイ時間が数十秒〜1分程度で、弊社より何倍も早く完了するみたいです。
このあたりのデプロイ高速化を見習って対応していきたいなと思っています。

まとめ

以上、ここまで簡単に説明してきましたが、メンテナンス性についてやデプロイ時間の改善などが未対応なものがいくつかあります。
新しい技術を取り入れてうまいこと運用しているサービスもよく見かけます。
モダンな技術を取り入れてインフラ面でも技術的な負債をなくし、管理のしやすい環境作りを心がけていきたいです。

まだまだ課題はたくさんありますので、一緒にインフラ改善をしてくれるメンバーも募集中です!
よろしくお願いします!

サービス成長との付き合い方

はじめに

サーバサイドとAndroidネイティブアプリの開発を兼務している坂根です。

webアプリ開発を行うエンジニアと聞くと、 要求された機能を実装する つまりディスプレイと睨めっこしながら黒い画面に文字をタイプし続けるコーディングが中心のお仕事、と考えられている方もいらっしゃると思います。 しかし、私は なぜ開発をするのか 開発をする目的は何か 背景の理解を大事にして働いています。

皆さんの身近にいるエンジニア、想像するエンジニアは、どのような働き方をされているでしょうか? 最近、背景の理解を大事にしていないエンジニアが案外多いのではないかと感じています。 今日は背景の理解を大事にするとはどういうことか、私の仕事を通して紹介させていただきます。

要件の分解・分析

弊社では主にwebディレクターが開発案件のビジネス要件を考えています。 ユーザーが抱える潜在的な課題の解決、リーチ・認知の拡大、リピート率の向上など、webアプリエンジニアには考えが及びづらい箇所を基点に日々議論されています。

しかし、考えが及びづらいが故にwebエンジニアは全容の掌握を怠ってしまう場合があります。

全容の掌握

全体の掌握を怠ってしまうと何が起こるのでしょうか? webディレクターが事前に検討しているため、降りてくる要件を実現するだけで 近い将来の目標 に到達することは難しくないでしょう。 しかし 数年後の目標 に自然な形で到達するためには、課題から数年後の目標までの道筋を設定した上で、中間に近い将来の目標を置く必要があります。 つまり全体の掌握を怠ってしまうと、発射角を誤り望んだ未来や自然な未来に辿り着かないケースに発展し、無駄に難易度の高い仕様変更を重ねていくことになると考えています。

数年後の目標

以上のことから、課題やターゲットを明確にして、より良い解決方法を求めることを大事にしています。

解決策の模索

解決したい課題と数年後の目標を明確にできたからと言って、直ちに最善の解決策を出せるとは限りません。 議論に挙がる解決策がユーザーに驚きを与えるものだとしたら、どうでしょう? 「使い方が分からない」「意味や意図が理解できない、不安」など、ユーザーにとってもサービスにとっても多くの混乱を招きそうです。 問い合わせや不満に繋がり、やがてはサービスからの離脱も・・・と考えられ、この状態では誰も幸せにならないことも容易に想像できます。

では、驚きを軽減するためどのような方法があるでしょうか? 驚き = 予期しない事象を体験したときに起こる瞬間的な感情 ということで、予期できる事象を考えます。 予期できる事象とは、同一サービス内で類似の体験がある、他サービスで体験しており既に親しみがある、などが考えられます。 類似の体験があることで学習コストは低くなり、ストレスも溜まりづらいなど副作用が期待できます。

コストの掛かり過ぎる解決策の場合はスモールスタートさせることもありますが、可能な限りユーザー体験を考えることも大事にしています。

妥当性の担保

ここまで考えたので、もうこれ以上考えたくない!という方もいらっしゃるかもしれませんがもう少しです。最後までお付き合いください。

これまでは下記の流れで考えてきました。 数年後の目標に向かって道筋が通ることを確認できたはずです。 しかし、この結論について妥当性が担保されていません。

トップダウン

そこで、逆向きに考えてみます。なぜその結論に至ったのか、根拠を確認します。 納得できる理由付けになっていたでしょうか?納得できる理由付けではない場合、ここで妥協してはいけません。 将来的に不幸になる人が出てくる結果に繋がるため、解決策の模索からやり直します。

ボトムアップ

おわりに

さて、ここまでお読みいただいたみなさま、いかがでしたか?

時間の無駄では?webディレクターの仕事では?案件のスピード感落ちない? などなど、いろいろな感想があると思いますが、以上がコーディング前に考えごとをする私の仕事の一部です。

目的を達成するために・・・私はこれからも背景や全容の掌握を大事にします。 また、その他大勢のwebアプリエンジニアにも大事にして欲しいことだと考えています。

このような働き方に共感・理解いただけるwebアプリエンジニアの方と働けるようになること、日々楽しみにしています。 ご興味ありましたら是非ご応募ください。

サーバーサイド開発からiOSアプリ開発チームに異動して感じたこと

f:id:jmty_tech:20201112100722p:plain:w150f:id:jmty_tech:20201112100756p:plain:w150

こんにちは!ジモティーでiOSアプリ開発を行っている小林です。

私は元々サーバーサイドエンジニアでしたが、2ヶ月ほど前にiOSチームにジョインしました。今回はiOSアプリ開発を始めたばかりの私が感じたRailsによるサーバーサイド開発との違いについて感じたことや、アプリの開発を始めて良かったと感じたことをご紹介したいと思います。

iOSチームにジョインした経緯

入社して約2年半Railsを使用してサーバーサイドの開発を行ってきました。Railsで一通りの開発が経験できたのと、Railsとは違った開発手法を体験したいという思いがありiOSチームへの異動を希望していたところ、その願いが叶えられiOSチームに異動することになりました。 今は約1ヶ月間の研修が終わり実際のアプリ開発を着手し始め、日々新しい学びがありワクワクしながら仕事をしています。

次からは、iOSアプリ開発を始めて感じたサーバーサイド技術との違いについてご紹介します。

iOSアプリ開発を始めて感じたサーバーサイド技術との違い

動的型付け言語 (Ruby) と静的型付け言語 (Swift) の違い

コンパイルの有無の違い

Rubyソースコードを変えるとすぐに結果を確認できますが、Swiftはコンパイルが必要なためコーディング⇄結果確認のサイクルに時間がかかるようになりました。その代わりにコンパイル時にバグを見つけることができたり、実装時にIDE (Xcode) が随時エラーを指摘してくれたりするため、実装の手戻りは減ったように感じます。

ダックタイピングできないためインターフェースの数が多くなる

SwiftはRubyと違いダックタイピングの機能がないため、同等の機能を実現するためにインターフェース(Swiftのプロトコル)を多用することになります。そのため、記述が冗長になったり、ファイル数が増加したりしますがエラーがコンパイル時点で分かったり、可読性が高いという利点があると思います。

nilの取り扱い

Swiftの利点として大きく感じたのはnilを許容するかしないかを型として明示的に表現できることです。Rubyではnilのチェックが漏れているため実行時にエラーになることが多々ありましたが、Swiftでは実装時やコンパイル時にエラーになり間違いに気づくことができます。その代わりとしてnilを許容するオプショナル型を扱うための構文を学習するコストが少しかかりました。

# Ruby
foo = nil
# ~中略~
foo.size # nilになる可能性があるが、ぼっち演算子(&.)をつけ忘れたため実行時エラー
// Swift
var foo: String  // String型
var bar: String? // nilを許容する Optional<String> 型

foo = nil // コンパイルエラー(fooはnilを許容しないため)
bar = nil // OK(barはnilを許容するため)

print(foo.count)  // => 0
print(bar.count)  // コンパイルエラー(barはnilを許容するが、値をアンラップしたり、オプショナル・チェインを使用したりしておらず、nilになることが考慮できていないため)
print(bar?.count) // => nil (Rubyのぼっち演算子に似たオプショナル・チェインという仕組みが用意されている)

Rubyで参照型なものがSwiftで値型になっている

Rubyでは参照型だったStringが、Swiftでは値型になっています。Swiftでは配列やハッシュまでもが値型なのには驚きました。しかし、値型になことになったことにより、Rubyで防御的にdupによるコピーを行っていたケースでコピーが不要になりました。また、Copy on Writeの仕組みによりデータが変更される時になってはじめてコピーされる仕組みのためパフォーマンスも確保されているようです。

# Ruby
foo = [1, 2, 3]
bar = foo
foo[0] = 5
p bar # => [5, 2, 3] (Rubyの配列は参照型のため、fooと同じ配列を参照しているbarも書き換わる)
// Swift
var foo = [1, 2, 3]
var bar = foo
foo[0] = 5
print(bar) // => [1, 2, 3] (Swiftの配列は値型のためbarは書き換わらない)

アプリケーション開発におけるフレームワークによる違い

弊社ではWebアプリケーション開発にRuby on Railsを使用しています。Railsの場合はRailsが敷いてくれたMVCをベースとしたCoC(設定より規約)のレールに乗り効率よく開発ができていました。 一方iOSのネイティブアプリケーション開発の場合は、標準でMVCをサポートしているものの自由度が高く、規約がない状態だと三者三様の書き方になってしまうこともあり、弊社では以前本ブログでもご紹介したClean Architectureを導入しています。 Clean ArchitectureがRailsより疎結合なことと、依存関係逆転のためのインターフェース(プロトコル)が多いことにより、1つのアクションを実現するために必要なファイル数が多くなっています。

設計で重視される部分の違い

前述の通りWebアプリケーションの開発ではRailsが敷いたレールに乗っていれば効率良く開発することができましたが、ビジネスロジックの設計の良し悪しが後々の改修の際の開発効率に影響することがありました。 逆にiOSアプリの開発では、ビジネスロジックは主にサーバー側にあるためビジネスロジックの設計が開発効率に及ぼす影響は少ないですが、アーキテクチャー設計の良し悪しが開発効率に及ぼす影響が大きいと感じました。

リリース方法の違い

サーバーサイド開発ではリリース、障害発生時の切り戻しは簡単に実施できましたが、iOSアプリのリリースは事前にAppleによる審査が必要なため簡単には実施できません。また、Appleガイドラインを満たしていないと審査がパスせずにリリースができないといったことが起こりえます。 そのため、予めリリース内容にガイドライン違反になるものがないかのチェックや、サーバーサイド開発よりテスト工数を多く確保し、複数人での実機を用いたテストを集中的に実施する期間を設けて品質を担保しています。

iOSアプリ開発を始めて良かった点

これまで行ってきたサーバーサイド開発に加えてiOSアプリの開発も始めたことにより、特に以下の点が良かったと感じました。

  • モダンな静的型付け言語であるSwiftを学んだことにより、プログラミング言語に対する知識が相対化され理解が深まった。
  • サーバーとアプリ両方のソースコードを把握することにより、サービス全体への理解や、問題が発生した場合の原因特定までのスピードが上がった。
  • サーバーとアプリのインターフェースであるAPIの設計について、開発着手後に仕様が変更された場合のアプリ開発への影響の大きさを理解することにより、その重要性が改めて認識できた。

おわりに

最後までお読みいただきありがとうございます。

ジモティーを更に成長させる為に一緒に取り組んでいただけるエンジニアを募集しています。 ジモティーのエンジニア組織は比較的少人数で一人ひとりの貢献がプロダクトの成長に大きく寄与するため、やりがいが大きいと感じています。また、上に記載した通り、チームの異動についても柔軟に対応してもらえるので色々な技術を習得することが可能です。 もし少しでも気になればお気軽にご応募ください。

ジモティーにおけるGoogle Apps Scriptの活用とclaspを使ってコード管理する方法

こんにちは、ジモティーでエンジニアをしている中村です。

以前の橋本の記事が好評だったので、今回もデータ活用周り、特に弊社でのGoogle Apps Script(以下GAS)の活用について書いていきたいと思います。

ジモティーにおけるGASの活用例

弊社ではGASを色々な用途で利用しています。ここではその一例をご紹介します。

KPIメールの送信

KPIメールとは、橋本の記事でもご紹介した通りサービスのコンディションを測るための重要指標を集計し、1日に2回全社員向けに送信しているメールのことです。 GASを使ってBigQueryやFirebase、SpreadSheetなどから各KPIを集計してそれをスプレッドシートとメールに出力しています。

リリース履歴の保存

監査のためにリリース履歴を保存する仕組みにも利用しています。 弊社ではreleaseというブランチを切っており、このブランチにマージされたものが本番にリリースされます。 リリース時はメインブランチからrelaseブランチ向けのプルリクを作ります。 このプルリクがマージされたときに、GitHubのWebhookでGASへリクエストを飛ばして、それを受け取ってリリース履歴保存用のスプレッドシートに書き出しています。

CircleCIのモニタリング

弊社ではCIツールとしてCircleCIを利用しているのですが、度々「CI遅くない?」「遅いよね」「うん遅い」みたいな会話が発生していました。 なので、本当に実行時間が伸びているのかを検証するためにGASでCircleCIのAPIを使って各ジョブの実行時間を取得し、スプレッドシートに書き出すことで平均実行時間をモニタリングできる仕組みを構築しました。

GASの導入理由

弊社ではデータ分析基盤にBigQueryやFirebase、SpreadSheetなどのGoogle系のサービスを利用しています。KPIメール配信システムの開発の際にそれらのサービスと連携がしやすかったためGASを導入しました。

GASのデメリット

ただ、GASを使い始め以下のような課題もありました。

  • Webエディタでの開発がしづらい
  • コードレビューがしづらい
  • 問題があったときに元の状態に戻しづらい

これらを解決するために、弊社では clasp というツールを導入しました。

clasp の導入

claspとは?

claspGoogle謹製の、GASをローカルで開発出来るようにするためのツールです。 pull コマンドでリモートのGASのコードをローカルに落としたり、push コマンドで簡単にアップロードが出来ます。 また、弊社ではいまのところ利用していませんが、TypeScript や ES6 も使えるみたいです。

弊社では、clasp でローカルに落としたコードを GitHub にプッシュすることでGASのバージョン管理をしています。

claspの使い方

最後にclaspの使い方を簡単にご紹介したいと思います。

1. claspのインストール

clasp は npm を使ってインストールします。

# claspをインストール
> npm i @google/clasp -g

2. clasp で Google アカウントにログイン

以下のコマンドでお使いのGoogleアカウントにログインします。

> clasp login

ブラウザが起動するので、シート作成時のアカウントを選択してclaspからのアクセスを許可してください。

3. Googleスプレッドシートの新しいシートを作成する

弊社では、GASとSpreadSheetを紐つけて管理しているので、SpreadSheetのスクリプトエディタからGASを起動していきます。

4. スプレッドシートのツールメニューから「スクリプトエディタ」を起動

スクリプトエディタを起動

GASのWebエディタが開きます。

5. GASのプロジェクト名を設定

適当な名前をつけてください。

GASのプロジェクト名を設定

6. 試しにスクリプトを実行してみる

↓のように関数を実装して、▶ を押して実行してみてください。

スクリプトを実行

表示メニューの「ログ」、もしくは Cmd + Enter で実行結果を確認できます。

実行結果

7. プロジェクトのプロパティのスクリプトIDをコピー

GASのスクリプトエディタの「ファイル」メニューの「プロジェクトのプロパティ」の、スクリプトIDをコピーしてください。

スクリプトIDをコピー

8. ローカルに適当な名前のディレクトリを作成

> mkdir clasp_sample
> cd clasp_sample

9. clasp clone でローカルにGASのコードをClone

> clasp clone 【手順7でコピーしたID】
Warning: files in subfolder are not accounted for unless you set a '.claspignore' file.
Cloned 2 files.
└─ appsscript.json
└─ コード.js
Not ignored files:
└─ appsscript.json
└─ コード.js

Ignored files:
└─ .clasp.json
> ls
appsscript.json コード.js

10. ローカルでファイルを編集してPush

function myFunction() {
  Logger.log("ぼくジモティー");
}

編集してプッシュします。

> clasp push
\ Pushing files...Push failed. Errors:
{ Error: User has not enabled the Apps Script API. Enable it by visiting https://script.google.com/home/usersettings then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

エラーがでます。

11. エラーメッセージにあるURLにアクセスして Google App Script API をONにする


12. もう一回 Push

> clasp push
└─ appsscript.json
└─ コード.js
Pushed 2 files.

今度は成功しました。 もう一度スクリプトエディタを確認すると。

プッシュ後のスクリプトエディタ

無事ローカルの修正がリモートに反映されています。

13. GitHubなどにPushしてバージョン管理

弊社ではGitHubを使ってGASのバージョン管理をしているので、最後に忘れず変更をPushします。

終わりに

最後まで読んでいただきありがとうございました。 本記事の内容が少しでもお役に立てば幸いです。

また、弊社ではジモティーを更に成長させる為に一緒に取り組んでいただけるエンジニアを募集しています。 もし少しでも気になればお気軽にご応募ください。

弊社iOSアプリにアーキテクチャを導入してみた

はじめに

サーバサイドチームに所属している丁(てい)です。

2016年の入社当初から今年の初めまで約3年半iOSチームに所属しながらネイティブアプリ開発をしていたので、今日はその中で弊社のiOSアプリアーキテクチャを適用した過程などを紹介できればと思います。

当時のことを思い出しながらさまざまな紆余曲折などをしみじみと回顧しつつご紹介していく文章になるとは思いますが、お付き合いいただけますと幸いです。

ちなみにこちらの記事弊社Androidアプリアーキテクチャを適用した内容が端的かつ整理された状態で紹介されているので、「お前のだらだら書く文章に付き合ってられねえわ」と言う方はこちらの記事をお勧めいたします。

当初の状況

私が入社した2016年5月時点での弊社iOSアプリは大体以下のような状況でした。

  • ほぼ全てのソースコードObjective-Cで書かれている。
  • 初期開発時にWebViewで提供していた画面をネイティブに置き換えている最中。
  • ネイティブに置き換えた物はvoidメソッドが無数に存在しながら副作用しまくってるFatViewController状態。
  • 自動テスト?何それ美味しいの??

このような状況で当時流行りだったMVVM(+RxSwift)をObjective-Cでネイティブに置き換えたトップ画面に対してSwiftでリプレースしつつMVVM(+RxSwift)を試験的に採用してみようと言うことになりました。

ここで今振り返ると悪手だったのは当時のリプレース実装担当者も含めて全てのチームメンバーがアーキテクチャ(MVVM)に対しての知見があまりないまま採用して導入しまったことにより、コードメンテナンスが非常に困難な複雑で巨大なViewControllerが誕生してしまったことです。

「目玉焼きが焼ける」

上記のような状況でしたがチームメンバーの血と汗が滲むような量的努力により、なんとかサービスを提供するアプリとしてのギリギリの体裁は保っていました。(と思いたい)

そんな折、弊社iOSアプリのAppStoreレビューにこのような文章が書かれるようになります。

・アプリ使ってるとiPhoneがめっちゃ熱くなるんだけど・・・
・使ってる最中にすぐ落ちるからイライラする。
・iPhone熱くなりすぎ。目玉焼き焼けるんじゃね?w

実際に我々メンバーもリリース前テストなどでアプリを使ってるとiPhoneが発熱し使えば使うほど重くなり、最終的にはクラッシュすると言う事象を確認してましたが、ソースコードが整理されていないためどこに原因があるかを探ることすら困難な状況でした。

結構な発熱になるので「携帯カイロ機能搭載と言う名目で売り出せばいいんじゃね?」って言うあまり笑えない冗談を言いながらも、この課題に真っ向から向き合うことが出来ずに悶々とする日々を送っていました。

Androidへのアーキテクチャ導入

弊社はiOSと共にAndroidアプリをリリースしているのですが、先に触れたようにAndroidアプリにもいくつかの課題があり、それを解決するために外部から技術顧問の方を招聘して知識を借りながら当時チームリーダーだったW氏を中心にソースコードの整理とアーキテクチャの導入が先行して進んでました。

iOSアプリはというと引き続き前述の状況が続き、アプリの発熱、クラッシュの頻発などに加えソースコードの複雑性によりリリースのたびにバグが発生して対応に追われるという辛い状況が続いてました。

そこで弊社の偉い人とW氏とK氏と私で課題解決に関する議論を行った末、Androidに導入した知見を元にiOSにもアーキテクチャを導入してまずソースコードの見通しをよくしつつその過程で発熱、クラッシュの原因を炙り出して潰していくという方針が定まりました。

採用アーキテクチャの選定

iOSアーキテクチャを導入するという方針が定まったことでようやくこの課題に対して一歩前に進める準備は出来ましたが、当時のiOSチームメンバーは正直「アーキテクチャ??何それ美味いの??」状態でした。

多少表現は大袈裟ですが、当たらずとも遠からずの状況でしたのでまずAndroidチーム所属だったW氏をiOSチームにコンバートして先陣を切ってもらいつつ、横で私がその知見を盗んで蓄積して他のチームメンバーに展開していくという作戦を立てることにしました。

まず最初に行ったのは「アーキテクチャ??何それ美味いの??」状態からの脱却のため、代表的なアーキテクチャをメンバーそれぞれが少し学習してからその内容を持ち寄って発表して、その中から弊社のアプリにマッチするものを選ぼうというものでした。

結論から言うとこの作業は無駄でした。 そもそもアーキテクチャに対する造詣が浅いので、多少MVVMやVIPERの記事を読んだところでその本質を理解できるわけがありません。

N氏と私でMVVMやVIPERに関する浅い知識を持ち寄って発表会を行いましたが全員いまいちピンと来なかったため、すでにAndroidアプリへの導入経験があるW氏の中にある程度知見のたまっているMVP(GUIアーキテクチャ)+CleanArchitecture(システムアーキテクチャ)を横展開で採用するということになりました。

※当初W氏が我々に説明するために書いた概要図がこちら。

f:id:jmty_tech:20201015120550p:plain
MVP+CleanArchitecture構想図

終わりに

この記事を書いている2020年10月時点の弊社iOSアプリはほぼほぼSwiftでリプレースされており、まだまだ完璧な形とは言い難く課題はたくさんあるものの、ある程度ルール化されたアーキテクチャに則ってチームメンバー間で統一された思想のもとで開発が進められているせいで、残念なことに極端な発熱による目玉焼き機能は現在失われております。

今回はアーキテクチャの導入に至った経緯などをだらだらと書いただけの記事になってしまいましたが、次回以降はできるだけハマったポイントなどや失敗とそれに対する解決策などを語ることで、あわよくば同じような悩みを抱えている人の一助になれれば良いなという思いを頭の片隅に置きながらだらだらと書いていこうと思います。

現在の状況に至るまでの過程をこれから何回かに分けてゆるく書いて行くつもりです。 次回は導入計画立案や導入当初にあった出来事などを書いてみようと思います。