picon's Tech Blog

Talkroom / BATON を開発しているpicon inc.のテックブログです。

オリタケイさんのRT企画(約9000RT!!)の抽選をやった話(プログラム付き)

こんにちは、piconのエンジニアの渋谷幸人です!

どうしてやることになったの?

今回、ご縁があって「オリタケイ」さんというとても素敵なイラストを書く方のお手伝いをしました。

内容は、「Twitter上でリツイートしてくれた人の中から数名の選んで、アイコン用のイラストを書くよ!」といったものでした。

想像以上の大反響で、ネット上にあるツールだと100RTまでしか取得できないということで...

絶望の果てにくれていた とのことだったのですが、たまたまお話する機会があってお手伝いすることになりました。   

※ 社長からもこちらの案件を快諾していただきました。

どうやってやったの?

TwitterAPIの制限が厳しくなっているという話は小耳に挟んでおり、もしかしたら難しいかもな...と思っていました。

その旨を伝えた上で、とりあえずチャレンジしてみました。

調べてみたところ、通常のtweetの取得の方法だと、RTを100件程度しか取得できないことがわかりました。

なんとかできないかと、調査を進めるとこちらの記事を発見しました。

[Twitter API] リツイートを100件より多く取得する方法 | プログラミング生放送

やりたいことドンピシャでした。

特定Tweetの詳細を取得するのではなく、文字列でTweetを検索することでページングを可能にして全件取得を目指します。

実際に開発してみる

あとはAPIの変更を実際に確認した上で、使い慣れているRubyを利用して実行しました。

twitterというGemを利用しています。

APIこちらのドキュメントを参考にしてます。(現在は15分あたり180回のリクエストまでの制限があります。)

以下が実際のコードです。

一度しっかり動けばいいので、強い実装にはなってないです...

client = Twitter::REST::Client.new do |config|
  config.consumer_key = "取得したものを入れる"
  config.consumer_secret = "取得したものを入れる"
  config.access_token = "取得したものを入れる"
  config.access_token_secret = "取得したものを入れる"
end

target_tweet_text = "ご要望に応じてこんな感じ(画像参照)のアイコンイラストを4名くらいの方にお描きします〜〜"
number_of_selection = 4
screen_names = []
finish_paging = false
max_id = nil


while !finish_paging
  sleep 0.1

  search_result = client.search(target_tweet_text, result_type: "recent", max_id: max_id)

  search_result.attrs[:statuses].each do |status|
    screen_names.append(status[:user][:screen_name])
    p status[:user][:screen_name]
  end

  
  if next_results = search_result.attrs[:search_metadata][:next_results]
    max_id = next_results.match(/\max_id=(\d+)&q/)[1]
  else
    finish_paging = true
  end
end

p screen_names.sample(number_of_selection)

まとめ

いつもお世話になっている人の役に立ててよかったです。

美味しいお酒でもおごってもらいたいと思います。


piconでは、一緒にプロダクトを作っていく エンジニア(特にiOS, Android)を 募集しています。

少しでも興味ある方は、ぜひ気軽にDMください!

まずは週末コミットやハッカソンのみの参加でも歓迎です。

↓ 連絡はこちら

twitter.com

BATONリリースで得たサイドプロジェクト開発における勘所

先日BATONというサービスをリリースしました。

piconは普段はiOSアプリの開発をメインでやっています。
そのためWeb経験が少ない2人での開発でした。

そんな中で、 なんとか1ヶ月弱でのリリースを実現出来ました。

baton.wiki

今回の記事では、BATONのプロジェクトでの学びをKPT形式で共有したいと思います。

BATONは週末ハッカソン発の サイトプロジェクトとして開発 をしていました。

社内で新規事業を考えている、非エンジニアの人も参考になる内容 になってると思うので、ぜひ読んでみてください。

【徹底解説】正しい「KPT」が仕事の成果を生み出す!進め方のコツ、現場の事例を紹介 | SELECK [セレック]

Keep

新技術を導入する際に経験者に手伝ってもらうのがよかった

新技術を軌道にのせる部分を初学者がやると、設計的にもスピード的にも悲惨なことになりがちだと思います。

今回はJavaScriptすら本格的に開発したことない中で、Reactを採用しました。

そのためReactでの開発経験のあるkotaroさんに、週末開発的な感じで立ち上げに入ってもらい、基本的な作法や技術選定などをリードしていってもらいました。

学習効率を最大化するためにも、長期的な開発速度を維持するためにも、経験者の方に立ち上げを手伝ってもらうのは、本当によかった と思っています。

新技術と使い慣れた技術の選定のバランスがよかった

今回の開発では、スピードは保ちつつも、開発力をアップするための学習も出来ました。

Webクライアントでは、Reactという新技術を使いつつ、そのほかの部分(API / インフラなど)では、徹底して既に経験のある技術を採用しました。

学習と開発スピードのトレードオフの中で、どういうバランスをとっていくのか大事な課題だと思うので、今後も向き合っていきたいと思います。

Problem

開発期間の見積もりが甘かった

当初は1週間程度でのリリースを予定していましたが、実際3週間ほどかかりました。

メイン事業へのリソースを投下できなくなってしまう

BATONはあくまでサイドプロジェクトでした。

なので メイン事業への開発リソースが最優先であるべき なので、リリースが想定よりも伸びたときにメイン事業の進捗に影響が出てしまうケースも考えられました。

リリースが伸びた時の対応を考えておくべきでした。

フロントエンジニアとのコラボレーションが改善の余地がありそう

PMでデザイナーのしょせまるに、デザインとフロントエンド(HTML/CSS)開発をしてもらいました。

HTML/CSSを書いてもらって、それをReactに移行するという形をとっていてたのですが、移行コストが無駄にかかっている感じがありました。

バグをリリース前に発見できなかった

リリース直後、Twitter認証が一部端末でうまく行かないバグが発生しました。

Try

新技術を利用した開発の見積もりは少し多めにとる

このツイートの通り、どの技術にも落とし穴がたくさんあることを実感しました...

そしてその落とし穴に見事にハマっていきました。(この経験が自分を強くすると信じてます...)

今後は、特に 新技術を採用する際はバッファーを多めにとった見積もり をしたいと思います。

リリースが遅れたときは投下するリソースを制限するようにする

今回は、BATON開発中のタイミングでは、メイン事業での開発タスクが多くなかったので実際には問題はありませんでした。

ただ、サイドプロジェクトに追われてメイン事業の進捗が遅れてしまったら元も子もないです。

もし期限が伸びてしまった場合は、週にx日しかサイドプロジェクトの開発に割かない、というような規約を決めておくといいかなと思いました。

もしくは場合によっては、開発を保留にしておいて手が空いたらやる、ぐらいの判断でもいいかもしれません。

周辺領域の知識を身につけた上で開発をする

今回のケースだとフロントエンジニアにReactについて少し学習してもらえれば、直接Reactでフロントを開発できるようになっていたと思います。

そうしていれば移行作業がなくなるだけではなく、CSSコンポーネントごとに書くこともできました。

以後は領域をまたぐ際の連携が発生する際には、学習コストを掛けてでも全体の開発効率化を図る選択肢を持っておこうと思います。

QAタイムを導入する

今回はしょせまるがサービスのチェックをする片手間で一人QAを担当していました。

それだと頻度の低いバグや分かりづらいバグを発見できない可能性がありますし、実際に認証という致命的なところで一部端末でバグが発生してしまいました。(遭遇された方、本当に申し訳ないです...)

以後は、 リリース前にQAタイム を導入したいと思います。

社員全員のみならず、バイトなどで人数を補填して、一気にバグをハントする時間を取ることでバグを事前に発見したいと思います。

まとめ

今回のBATONプロジェクトはエンジニアの開発力アップという面でも、piconとしての打ち手の選択肢を増やすことが出来たという面でも、いい挑戦だったと思っています。

この学びをメインの事業にも反映しつつ、これからも開発を進めていきます。


piconでは、一緒にプロダクトを作っていく エンジニア(特にiOS, Android)を 全力で募集しています。

少しでも興味ある方は、ぜひ気軽にDMください!

まずは週末コミットやハッカソンのみの参加でも歓迎です。

↓ 連絡はこちら

twitter.com

TalkroomのiOS高速開発を支える開発規約

こんにちは!piconの唯一のエンジニアの渋谷です!

piconでは、現在エンジニア一人の体制で開発を行っています。

事業拡大にむけて開発クオリティ・スピードともに全く足りていないので、 エンジニア(特にiOS, Android)の方を強く募集 してます!

 twitter.com

ただそんな体制の中でも、僕が その他の業務で忙殺されていない限り 以下のような比較的ハイペースで開発が実現出来ています。

  • Talkroom の初期バージョンは約1週間

  • 大型アップデートも1〜2週間程度

今回は、そんなpiconのiOS開発を支える開発規約について紹介したいと思います!

規約をきっちり決めている

前提としてpiconのiOS開発では、規約が比較的きっちり決まっています。

一人でもあえてがっちり決めてます。

規約が決まっていると、「どこにどんな風に書くか」など不要なところで思考をする必要がなくなるので、UIとかであればほぼ無心で開発できるようになりました。

 

読みやすさもグンと上がるのでとても気に入っています。(3ヶ月後の自分は他人であるのはなんども痛感しました...

 

ここでいう規約は、改行や記法の規約にとどまらず、レイヤーの分け方、ファイル内での関数の置き方、変数の定義の仕方など、比較的広い意味での規約です。

 

今回は、汎用性の高そうなUI周りの規約について紹介したいと思います。

※ PCでご覧になることをおすすめします...

 

UI周りの規約

picon ではストーリーボードを利用していません。ここは賛否両論あると思うので一旦触れずにおきます、、、w

 

UIViewControllerのクラスには、Viewの見た目とレイアウトに関することを記述します。

Viewだけにとどまらない処理については、ViewModelで処理するようにしています。

 

UIViewのサブクラスは以下の基準のいずれかを満たす場合にのみにしてます。

  • 処理もしくは構成が複雑である

  • 通化できるものである

  • 汎用性が高いものである

 

可読性を損ねるので、自作のUIViewサブクラスが自作のUIViewサブクラスを持たないようにしています。

 

UIViewControllerクラスのテンプレートは以下です。

 

import UIKit
import RxSwift

final class SampleViewController: UIViewController {

    // MARK: - Views -
    

    // 説明メモ:viewはlazyで定義して、プロパティの初期設定はここで行う
    // 各Viewのプロパティがかたまりとしてひと目で確認できるのが便利

    private lazy var sampleView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor.black
        view.clipsToBounds = true
        view.layer.cornerRadius = 10
        return view
    }()
    
    
    // MARK: - Properties -
    
    private let viewModel: SampleViewModelType
    private let disposeBag = DisposeBag()
    
    
    // MARK: - Initializer -
    
    // 説明メモ:viewModelは外部から注入する

    init(viewModel: SampleViewModelType) {
        super.init(nibName: nil, bundle: nil)
        
        self.viewModel = viewModel
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    // MARK - Life Cycle Events -
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configure()
        setViews()
        setConstraints()
    }
    
    
    // MARK: - Setup -
    
    // 説明メモ:UIViewControllerに関する設定など

    private func configure() {
        view.backgroundColor = UIColor.white
    }
    
    // 説明メモ:viewへのaddはこちらで

    private func setViews() {
        view.addSubview(sampleView)
    }
    
    // 説明メモ:viewのレイアウトの制約はこちらで

    private func setConstraints() {
       sampleView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    }
    
    // 説明メモ:viewのイベント処理はこちらで(piconではRxSwiftを利用してます)

    private func subscribeViews() {
        let tapGesture = UITapGestureRecognizer()
        view.addGestureRecognizer(tapGesture)
        
        tapGesture.rx.event
            .subscribe(onNext: { [weak self] () in
                // 説明メモ: 簡単な処理ではない限り、関数に切り出す
                self?.createUser()
            })
            .disposed(by: disposeBag)
    }
    
    // 説明メモ: viewModelのイベントはここでsubscribeする

    private func subscribeViewModel() {
    }
    

    // MARK: - Other -
    
    private func createUser() {
        // Do something.
    }
}

// 説明メモ:Protocolに準拠させる場合はextensionを利用して分離させます

extension SampleViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // ...
    }
}

 

ここまで決まっているとどこに何を書くのか迷いなく書けるようになります。

新たな技術を触る時に、ボイラープレート + コード規約が公開されてると便利だなって思ってます。

ぜひ参考にしてみてください!

おまけ

おまけにViewModelのテンプレートも掲載しておきます。こちらはオープンソース化されているkick starterのコードを参考にしています。

参考記事:

Kickstarter-iOSのViewModelの作り方がウマかった

inputとoutputを明示的にわかりやすくなるのでとても気に入ってます。

import RxSwift

protocol SampleViewModelInputs {
    let submitName: PublishSubject<String> { get }
}

protocol SampleViewModelOutputs {
    let submitedName: PublishSubject<Void> { get }
}

protocol SampleViewModelType {
    var inputs: SampleViewModelInputs { get }
    var outputs: SampleViewModelOutputs { get }
}

final class SampleViewModel: SampleViewModelType, SampleViewModelInputs, SampleViewModelOutputs {
    
    // MARK: - Properties
    
    var inputs: SampleViewModelInputs { return self }
    var outputs: SampleViewModelOutputs { return self }
    private let disposeBag = DisposeBag()

    
    // MARK: Inputs
    
    let submitName = PublishSubject<String>()
    
    
    // MARK: Outputs
    
    let submitedName = PublishSubject<Void>()
    
    
    // MARK: - Initializers
    
    init() {
        setBindings()
    }
    
    
    // MARK: - Binds -
    
    private func setBindings() {
        submitName
            .subscribe(onNext: { [weak self] (name) in
                guard let me = self else { return }
                
                self?.updateName(name: name)
                    .subscribe(onNext: { [weak self] _ in
                        // 説明メモ:処理
                        submitedName.on(.next(()))
                    })
                    .disposed(by: me.disposeBag)
            })
            .disposed(by: disposeBag)

    }
    
    
    // MARK: - Other -
    
    private func updateName(name: String) -> Observable<Void> {
        // ...
    }
}

 ---

piconでは、一緒にプロダクトを作っていく エンジニア(特にiOS, Android)を 全力で募集しています。

少しでも興味ある方は、ぜひ気軽にDMください!

まずは週末コミットやハッカソンのみの参加でも歓迎です。

↓ 連絡はこちら

twitter.com

Talkroomを運営する picon inc. のテックブログをはじめました

こんにちは、渋谷幸人です。 この度、元nanapi CTOの和田さんにおすすめしていただいたこともあり、piconでテックブログを始めることにしました。 今回の記事では、初回ということで、テックブログを始めた背景の説明と、piconについて簡単に紹介したいと思います。

piconについて

普段やっていること

piconは、TalkroomやBATONといった主にティーン向けのサービスを作っている会社です。ワクワクするプロダクトを生み出すlab的な組織をめざしていて、企画・デザインも含めたプロダクトの開発力を強みにしています。

Talkroom

BATON baton.wiki

piconはこれまで、代表でデザイナーの山口とエンジニアの僕の2人でプロダクトを作ってきたのですが、この度3人目のメンバーとして新たにデザイナーが増えたこともあり、そろそろ死に…(以下略)

どうしてブログをはじめたの?

はじめた理由は3つあります。

  • piconのことを知ってもらって、エンジニアを採用したい
  • 思考や学びの整理をする
  • まとまった考えを伝えるのにはブログが今も有益だと思うから

piconのことを知ってもらって、エンジニアを採用したい

自分のことを知ってもらって、興味を持ってくれた方といつか一緒に働けたらと思いはじめました。 piconにおいて、今は僕一人でなんとか回っているのですが、いずれは技術レベル・開発工数的に、立ち行かないときが来ると思っています。

今現在も少しでもいいプロダクトをユーザーに届けるために、一緒に働いてくれるエンジニアの方を探しています。

ブログで役に立つようなことを発信しながら、僕たちのことを知ってもらい、興味を持ってもらえると嬉しいなと思っています。

ブログを通じて、考え方や文化を知った上で興味をもってもらえた人との出会いにはとても価値があると思っています。そういう意味でもブログはとても意義のあるものだと思っています。

思考や学びの整理をする

普段の業務で忙殺されていると、なかなか落ち着いて学びを体系化する余裕がないと思います。 ブログというアウトプット先を持つことで、混沌としていた状況を整理し、反省や学びをまとめる機会をもつことは長期的に見てとても意義のあるものなのではないかと思っています。

まとまった考えを伝えるのにはブログが今も有益だと思うから

今の時代であればTwitterも考えを伝えることができるツールだと思うのですが、それでもブログをはじめる価値はあると思っています。 140字の制限の中では思っていることのすべてを伝えられずにミスコミュニケーションが生まれてしまう可能性はありますし、ものごとを体系的に伝えるのには向いていないと思っています。(後者は技術系の発信をする際においては致命的)

さらに記事がアセットとして溜まりやすく、長期的な目線でも採用に効いてくるという点もあります。 そのような点でブログである意義が十分あると判断して、このブログを開始しました。

発信する内容について

主に以下の3つの方向性で発信していきたいと思っています。(MECEではないのですが)

  • 技術的な学び
  • スタートアップ開発現場のリアル
  • 組織づくり的な学び

技術的な学び

僕はどこかの技術に深く精通しているわけではないので、わかりやすさを重視した入門編的な記事とよくある落とし穴を回避する方法あたりを書ければと思っています。

スタートアップ開発現場のリアル

外からだとわかりづらいスタートアップの開発現場を発信できればと思っています。 どういう開発フローをとっているのか、デザイナーとの協業の方法、技術への向き合い方などスタートアップの良さ・辛さを交えつつ、日々どう業務に向き合っているのかを知ってもらえたらと思っています。

piconの大切な思想として、すべてを素直にオープンにするというものがあります。リアルを発信すると、多少不都合な部分もあると思いますが、そういうのも含めてスタートアップです。入ってから後悔するよりもお互いのことをしっかり理解した上で、働きたいと思うので、他のブログよりもよりリアルに近いものを発信できればと思っています。

組織づくり的な学び

上の項目にもかぶってくるのですが、これからエンジニア組織を作っていくにあたって、いろいろ学んだり課題にぶつかって行くと思います。 1人のエンジニアしかいないところから、魅力的なエンジニアを組織を作っていく奮闘日記的な形で、楽しみにながら学びの多い内容にできればと思っています。

まとめ

どのぐらいの頻度で発信できるかわからないのですが、少しでも読んで良かったと思ってもらえるブログを書いていきたいと思います。 よろしくお願いします。