「入門 考える技術・書く技術」

直近の業務において、技術的な文章を書く機会が増えてきているため購入。
わかりやすい文章の書き方、というよりもわかりやすい論理の組み立て方を伝えている書籍。


わかりやすく書くコツとしては大体下記4つを書いていた。

  • 読み手の疑問に対する答えを書く。
  • ピラミット型に論理を組み立て、主メッセージを明確にしたあとに、それを説明するメッセージを伝える。主張->根拠->更に詳細な説明、といったような構造。
  • あいまいな言葉を使わない。
  • 2文をつなげるためだけのand接続詞(〜し、〜であり)を使わずに、ロジカル接続詞(~ために、といった因果関係を示すものや、~である一方といった対象を表すもの)を使う。

ロジカルシンキングの一般的な内容で、過去そのような書籍は読んでいたことがあるため、自分にとって真新しいことは特になかった。
また、自分の目的である技術的なことをわかりやすく伝える術を学ぶ、というのとは少し違った。
技術的な文章を書くにしても、あとは実践をたくさんするしかなさそう。

過去読んだロジカルシンキングの書籍


とはいえ自分は接続詞の使い方だったり、あいまいな言葉は使ったりしがちなので振り返る良い機会にはなったとは思う。

Kaigi on Rails 2023に参加した

はじめに

2023/10/27-10/28に開催された Kaigi on Railsに参加しました。
kaigionrails.org

Ruby関連のカンファに参加するのは今年の松本でのRuby Kaigiに引き続き2度目です。

前回のRuby Kaigiでは弊社のブース担当として参加していましたが、
今回はブースのお仕事がないので発表を聞くのに専念できる・・・!ということで、ワクワクしながら参加してきました。

発表

自分が特に印象に残っている発表は以下の2つです。

やさしいActiveRecordのDB接続のしくみ

speakerdeck.com

私が業務でConnectionPool周りにパッチを当てている部分を修正する機会(※)があり、内部実装を見にいった経験がありました。
当時、内部の仕組みを理解するのが複雑で苦戦していたので、非常に楽しみにしていた発表です。
登壇者の方はrdbgでブレークポイントを貼り、DB接続に関しての主要なクラスをあぶり出し、図解で説明してくださいました。

自分で実装を追っていった際に概要を掴むまでにしばらく時間がかかっていたのですが、本当ににこの発表がわかりやすく業務で担当する前に見ておきたかったです。笑


※自分が業務で触った内容は、AWS AuroraのFailover時にConnectionPoolで古い接続を取ってきてしまう影響でDB接続できなくなる不具合の解消のために上記の実装を追ってました。
下記の記事でも紹介のある、mysql2-auroraというgemを使うことで解決しています。(上記の接続ができないときのエラーメッセージをハンドリングして再接続するgem)
qiita.com

32個のPRでリリースした依存度の高いコアなモデルの安全な弄り方

speakerdeck.com

オンラインDDLの対象操作を整理・挙動の注意点を説明し、実際のアプリケーションのデプロイフローなどを考慮すると

カラム追加 → 値の同期 → バックフィル → Not Null制約 …(略)… → カラムの削除

のように丁寧に一個ずつ手順を踏んでリリースしていくしかないね、というお話です。

最後にあった「モデルの拡張性・柔軟性について」の話はとても共感しました。

「将来的にはこうなりそう」と予想して不確実なものを設計に組み込むよりも、今ある情報で確実に設計していくべき。
テーブル変更定義は大変で危険だが、安全にリリースするためのDBの機能や実践例はたくさんあるし、それらを学んで行き、その技術を身に着けておくことが大事。

私自身、業務での新規機能開発の頻度は高く、テーブル設計の際には、企画者に対して「将来こういった機能の実装予定はありますか!?」という質問を投げかけて困らせがちだったり、機能を拡張する際にテーブル設計がしんどくて実装が辛くなったりすることも経験として多くありますが、上記思想は大事にしていきたいし、勉強していきたい。

補足

当日の発表については下記のブログに掲載されていました🙌
qiita.com

まとめ

今回Kaigi on Railsに参加してみて、自分の興味軸としては、フレームワークRailsがどうこう、っていう話ももちろん面白いけど、そもそもの根本のテーブル設計やDBあたりの話に結構向いているかも?と思って聞いてました。

また、学生時代にインターン先でお世話になった方や、前回のRuby Kaigiで知り合った方とも再会でき、とても嬉しかったです。

ほんとこれです。

自分はこれからもRubyにお世話になることが増えていきそうな予感だし、自分もアウトプット出していきたい。

「エンジニアリングマネージャーのしごと」を読みました

はじめに

なんと3年ぶりにブログを書く✏️
一年間とすこし務めたリードエンジニア業が終わり、再び開発に集中できる環境になってきたのでこれからまたアウトプットしていこうと思う。

ちなみに私の経歴としてはクライアントエンジニアから始まり、サーバーサイドエンジニアとリードエンジニア、採用業務を経験し、現在はまた開発中心の生活に戻って、サーバーサイドを中心にやっており、新卒から三年半、ライブ配信サービスの開発に日々取り組んでいます😎


5月に松本で参加したRubyKaigiのイベントにて、@TimeeDevさんから「エンジニアリングマネージャーのしごと」を頂きました!

結構ボリューミーな書籍で、かつ自分のためになって実践したポイントがいくつかあったので紹介していく✌️

本の構成

第1部 オリエンテーション
まず、マネジメントをする前に自分の業務の整理術に関してのパート

  • 1章 新たな冒険
  • 2章 まず自分を管理しよう

第2部 個人と働く
チームメンバーと働く上でのすべてのことに関してのパート

  • 3章 人間と関わる
  • 4章 1on1
  • 5章 その人に合った仕事とは
  • 6章 1年でいちばん輝かしい季節
  • 7章 採用中!
  • 8章 ゲームオーバー
  • 9章 友人を作り、人に影響を与えるには

第3部 全体像
プロジェクト・職場全体での働き方に関してのパート

  • 10章 人間って難しい
  • 11章 プロジェクトって難しい
  • 12章 情報の証券取引所
  • 13章 コントロールを手放す
  • 14章 良いハウスキーピング
  • 15章 デュアルラダー
  • 16章 現代の職場環境
  • 17章 スタートアップ
  • 18章 クリスタルボール

読書メモ

2章 まず自分を管理しよう

手際よく業務を進める上での整理方法についてまとめられています。

自分はこれをきっかけに以下を整理しました。

  • 机の整理
    • 机に収納ボックスを取り付けて、小物類が目に入らないようにした

  • 頻度の高すぎる1on1の整理
  • 不要なメールのサブスク解除
  • 朝読むリストの作成・ルーティン化
    • notion Web Cliperを使うことで、朝見返しやすく

www.notion.so

3章 人間と関わる

リーダーが自分自身で大事なタスクを全てやろうとすると、膨大すぎてどうにかなってしまいます。
そうならないための、タスクの委譲(タスクの責任を自分から部下に移す)について記載がありました。

  • やるべきこと
    • メンバーに取ってチャレンジングになるくらい委譲する
      • チームができることが増え、メンバーの能力も増え、WinWin
  • やってはいけないこと
    • 渡したタスクを散り返す
      • タスクの学習機会はもちろん、感情も害する
    • 他人のやりかたが自分と同じであることを期待する

リベラルアーツ大学の動画、 ついていきたいリーダーvs距離をとりたいリーダー【部下たちの本音】【人生論】でも似たようなことを言っていたことを思い出した。
youtu.be

任せるって心配だけど、体験して失敗することで、メンバー自身が学ぶきっかけにもなるし、リーダー業務する上で一番意識しながら働いたポイントかもしれない。

7章 採用中!

本書に記載の通り、エンジニアが全然足りない最中にいた私は、まさに「ただエンジニアを増やそうとする」「できる限りシニアな人を雇おうとする」「自分と似ている人だけを雇おうとする」に陥っていたw
そのため、面接の際の自分の質問が人によってぶれていたり、後からこれ聞いておけばよかった、となることがあった。

  • モチベーション
  • コミュニケーションスキル
  • 学ぶことへの前向きさ
  • 会社と価値観・倫理観の一致
  • どういったスキルの人がいればチームが伸びるか

まずはカルチャーフィットの特徴である上記を弊チームに合わせて定義することによって、
じゃあこのスキルを持っているか判断するにはどういう質問をしたら良いのか?と逆算した質問リストを作成することができ、一定の質で面接が進行できるようになった。

13章 コントロールを手放す

タスクの委譲をすることで、期待した品質でできるのか?進捗は大丈夫なのか?ということに関して心配になり、おせっかいを焼いてしまうしまったり、気になってしまってしょうがないことが多々合った。
自分でコントロールできる内部ゴール自分でコントロールできない外部ゴールを把握することで、自分がコントロールできるものにだけ目を向けることで精神を保つことができる手法を学んだ。

例) 転職をしているメンバーがいる
外部ゴール: 転職を思いとどまらせたい(自分でコントロールできない)
内部ゴール: できるかぎり良い環境を提供することにベストを尽くす(自分でコントロールできる)

まとめ

本の中では長々と記載している部分もあったが、マネージャーの視点に限らず、1エンジニアとして、個人の業務~プロジェクトの進行において円滑に進めるためのストーリがたくさん載っていた。
また業務で気になる観点があれば読み返してみたいと思う。

【Flutter】チーム開発でflutter_blocを導入したらめちゃめちゃ良かった

はじめに

研修でのチーム開発にて、某画像検索アプリのクローンのクライアント側をFlutterで実装しました。

状態管理手法としてBLoCパターンを採用し、その際にflutter_bloc というパッケージを使用しました。

使用感として

  • 誰が書いても同じコードスタイルになる
  • テストがしやすい

という印象をもち、大人数で開発する上では非常に使いやすいと感じたのでご紹介したいと思います。

BLoCとは🤔

BLoCとは Business Logic Component の略で、簡潔に説明するとUIからビジネスロジックを分離する設計パターンです。 2018年に開かれたDart Conferenceで発表されています。

BLoCの全体像やガイドラインに関しては、以下の記事が非常にわかりやすいのでぜひご覧ください。

【Dart/Flutter】導入したBLoCパターンアーキテクチャについて全体像をまとめてみた

このBLoCパターンを実現するには、DartのStreamを用いてデータを送り、providerを用いて、必要なWidgetにDIする手法がよく使われるかと思います。

以下参考記事。

長めだけどたぶんわかりやすいBLoCパターンの解説

flutter_bloc

flutter_blocは、上記で説明したBLoCデザインパターンの実装を容易にすることができるpackageです。 手法としては、UI側から何かしらのeventを送ることで、blocを通じてstateを変更するという方法で状態を変更しています。

[引用: https://pub.dev/packages/bloc]

ディレクトリ構造

lib/
 ├ api/ 
 ├ data/ 
 ├ model/
 ├ util/
 ├ values/
 ├ view/
  └ main/
    └ home/
           ├ home_widget.dart
         └ bloc/
              └ bloc.dart
              └ home_bloc.dart
              └ home_event.dart
              └ home_state.dart

eventの定義

// home_event.dart

import 'package:equatable/equatable.dart';

abstract class HomeEvent extends Equatable {
  @override
  List<Object> get props => [];
}

class LoadData extends HomeEvent {}

データを変更させるためのイベントを定義します。

stateの定義

// home_state.dart

import 'package:equatable/equatable.dart';
import 'package:path/model/book_model.dart'; //ここのパスは適宜合うものに変える

abstract class HomeState extends Equatable {
  @override
  List<Object> get props => [];
}

class LoadingState extends HomeState {}

class LoadedState extends HomeState {
  LoadedState(this.books);

  final List<BookModel> books;

  @override
  List<Object> get props => [books];
}

class NoDataState extends HomeState {}

class ErrorState extends HomeState {
  ErrorState(this.exception);

  final Exception exception;
}

アプリの取りうるState(状態)を記述します。 StateにてEquatable packageを使う理由としては、 propsに入っているプロパティが一致しているかを判断することができ、これらの値が全く一緒であればイベントをaddしたとしても、Streamは流れないようにすることができるからです。(間違っていたら指摘してください。)

blocの定義

// home_bloc.dart

import 'package:bloc/bloc.dart';
import 'package:path/data/books_repository.dart';
import 'package:path/model/book_model.dart';

import 'bloc.dart';

class HomeBloc extends Bloc<HomeEvent, HomeState> {
  HomeBloc(this._booksRepository);

  final BooksRepository _booksRepository;

  @override
  HomeState get initialState => LoadingState();

  @override
  Stream<HomeState> mapEventToState(HomeEvent event) async* {
    if (event is LoadData) {
      try {
        final books = await _booksRepository.getBooks();
          
        if (books.isEmpty) {
          yield NoDataState();
          return;
        }

        yield LoadedState(books);
      } on Exception catch (e) {
        yield ErrorState(e);
      }
    } 
  }
}

mapEventToStateにてどのイベントがきたら、どのような状態の変化をするのかを記述します。

また、以下のようなファイルを準備しておくことで

// bloc.dart

export 'home_bloc.dart';
export 'home_event.dart';
export 'home_state.dart';

widgetblocをimportする際は、以下のような記述で済むようになります。

import 'package:path/home_widget/bloc.dart';

widgetでのBlocの使用方法

BlocProviderwidgetを用いることで、BlocクラスをDIします。

// home_widget.dart

Widget build(BuildContext context) {
    return BlocProvider<HomeBloc>(
      create: (context) =>
          HomeBloc(context.repository<BooksRepository>())..add(LoadData()),
      child: : _buildScreen(context),
    );
  }

add(EventClass())メソッドを使うことによってイベントを発火することができます。

またDIした後は、BlocProvider.of<HomeBloc>(context);のようにblocを呼び出すことが可能です。

BlocBuilderwidgetを用いると、状態によってwidgetを変更することが出来ます。

Widget _buildStaggeredGridView() {
    return BlocBuilder<HomeBloc, HomeState>(builder: (context, state) {
      bloc = BlocProvider.of<HomeBloc>(context);
      if (state is LoadedState) {
        final books = state.books;
        return // booksを使ったwidget
      } else if (state is NoDataState) {
        return // データが無いときの表示処理
     } else if (state is LoadingState) {
        return // ローディング中の表示処理
     }
      return Container();
    });
  }
}

BlocConsumerwidgetを用いると、状態によって処理を分けることができます。

Widget _buildScreen(BuildContext context) {
    return BlocConsumer<HomeBloc, HomeState>(
        listener: (context, state) {
      if (state is LoadedState) {
        Navigator.pop(context); // データ取得処理が成功した場合のみpopする
      }
    }, builder: (context, state) {
      return // widgetを生成
    });

詳しくはflutter_blocをご覧ください。

テスト

テストに関するサポートもばっちりです。

bloclibrary.dev

詳しい説明は割愛しますが、次のようにStateをテストすることができます。

 blocTest<HomeBloc, HomeEvent, HomeState>(
        'テストの説明',
        build: () async {
        // blocを生成
        // モックの作成等はここで行う
      return HomeBloc(mockPinsRepository);

    }, act: (bloc) {
        // イベントの発火
      bloc.add(LoadData());
    }, expect: <HomeState>[LoadingState(), LoadedState(books)]); //期待されるState
}

気になるポイント🙋‍♀️

  • 小規模なアプリや個人の開発だと冗長な処理になりがち

この場合はsetState()StateNotifierパターンなど、もっとシンプルなものを選べば良いと思います。

  • 記述量が増える

bloc、event、state用のクラスをわざわざ作成する必要があり、記述量がどうしても増えてしまいます。 コード生成ライブラリであるhttps://pub.dev/packages/freezed freezed packageを活用するのも一つの手です。

以下のサイトにはflutter_blocにてfreezedを使用した例が書かれています。

Flutter - bloc with freezed

まとめ

flutter_blocではbloc、event、statteを定義し、そこからデータをwidgetに流すという方針をとっているため、誰が書いても似たようなコードスタイルになりました。 blocのUIガイドラインには、

Each "complex enough" component has a corresponding BLoC.

という記述があります。

今回は1widgetに対して1blocを用意するという原則を守ることで、どこに何の処理が書いているかを判別しやすくなりました。

複数人で開発する際には非常に役に立つpackageだと思うので、是非使ってみてください。

「リファクタリング - 既存のコードを安全に改善する」を読んでみた

おすすめされていた本の、「リファクタリング - 既存のコードを安全に改善する」を読んでみました!

本の内容

前半はJavaScriptで書かれたコードを題材に、この本で紹介されている原則に則ってリファクタリングをしていきます。

後半では前半のリファクタリングの際に用いた、様々な原則を丁寧なリファクタリング手法と共に説明されています。

より良いコードを書くためのテクニックをまとめた本といえば、「リーダブルコード」は名著として有名ですよね。

私は「リーダブルコード」も何度か読んでいますが、この本と比較すると「リファクタリング」はより具体例が豊富で、辞書的にも使いやすそうな書籍だなあという印象を持ちました。

参考になった具体例

確かに読みやすさが変わるなと思った項目を適当に2つ紹介していきます。

ファクトリ関数によるコンストラクタの置き換え

コンストラクタだけで初期化すると少し戸惑ってしまいそうな処理を、ファクトリ関数として置き換えることでよりわかりやすくできる原則のこと。 私自身はわりとシンプルにコンストラクタを使いがちだったので、これだけで扱いやすさがだいぶ変わるなという印象。

リファクタリング適用前
// sample.js

leadEngineer = new Employee(document.leadEngineer, 'E');
リファクタリング適用後
// sample.js

leadEngineer = createEngineer(document.leadEngineer);
     
func createEngineer(name) {
     return new Employee(name, 'E');
}

委譲によるスーパークラスの置き換え

継承は既存の機能を再利用するのに強力な手法ですが、 何でもかんでも継承をしてサブクラス化してしまうと、ただ処理が複雑になってしまったり、使われない操作までもが継承されてしまう場合がありますよね。

古典的な継承の失敗例として、StackをListのサブクラスにしていることが挙げられていました。(Javaを例にすると、実際に継承されているのはVectorで、VectorがListのinterfaceを実装しているもよう。) Stackを実装するために、Listのデータ構造や処理を再利用するために継承する設計になっていたようですが、Listの操作はStackにあまり適合していないイメージ。 docs.oracle.com

//sample.java

Stack<Integer> stack = new Stack<Integer>();
stack.push(2);
stack.add(1);

たしかにJavaだとStackにVectorクラスのaddが実装されているのは気持ち悪い…。

C#のStackのドキュメント見てみたら、こっちのStackは割とシンプルで良さそうだった。 docs.microsoft.com

話は逸れたが、スーパークラスの関数をサブクラスで活用出来ないような場合は継承するのではなく、Listをフィールドに保持しておき、必要な操作はStackに任せたほうがスッキリする。

リファクタリング適用前
// sample.js

class List {
// 具体的な処理
}
       
class Stack Extends List {
// 具体的な処理
}
リファクタリング適用後
// sample.js

class Stack {
          constructor() {
              this._storage = new List();
          }
       }
       
class List {
// 具体的な処理
}

まとめ

具体的な改善例が丁寧に書かれていて、納得感が多い事例ばかりでした!

長いけど、読む価値はあると思います!!!

FlutterでMockitoを使うときにレスポンスが返却されない問題

問題

  final Client _client;

  Future<Response> get(String relativeUrl, {Map<String, String> query}) async {
    final token = await _authenticationPreferences.getAccessToken();
    final header = {'token': token};
      _client.get(
        Uri.http(domain, relativeUrl, query),
        headers: header,
      ),
    );
  }

このように定義しているメソッド(headerを付与してGETリクエストを送る)に対して、テストを行う。

  group('ApiClient error handling', () {
    setUp(() async {
      //色々セットアップ
    });

    test(
        'when API return response with status code 401, [get] function should throw [UnauthorizedError]',
        () {
      when(mockHttpClient.get(any, headers: {}))
          .thenAnswer((_) => Future.value(Response('body', 401)));

      final getFuture = apiClient.get('401');
      expect(getFuture, throwsA(isInstanceOf<UnauthorizedError>()));
    });

mockito | Dart Packageを用いてテストを行うと以下のようなエラーが帰ってきていてレスポンスが返却されない。

NoSuchMethodError: The getter 'statusCode' was called on null.
Receiver: null
Tried calling: statusCode

解決策

      when(mockHttpClient.get(any, headers: anyNamed('headers')))
          .thenAnswer((_) => Future.value(Response('body', 401)));

      final getFuture = apiClient.get('401');
      expect(getFuture, throwsA(isInstanceOf<UnauthorizedError>()));


headers: anyNamed('headers')
ここが重要。

dart - Flutter/Mockito. Testing API provider with mockito, problem with api headers - Stack Overflow
こちらのStack Overflowによる実際のリクエストと合う形にしないとレスポンスが返却されないもよう。

最近読んだ本や漫画と積読書

今年は2記事/月は書く!と言っていたのに全く記事を書いていなかったので、
最近読んだ本とかについてゆる〜〜〜〜く書きました。
技術書やエンジニアの方が読むと面白い書籍が多い気がします。

読んだ📖

岩田さん 岩田聡はこんなことを話していた。

 
任天堂の岩田元社長のことばを集めた書籍なのですが、
エンジニアとして人としても岩田元社長の姿勢というか考え方を意識していきたいと思えるので定期的に読み返している一冊!
個人的には「MOTHER2」開発中のエピソードが衝撃的だったーーー。かっこよすぎるよ!!

f:id:yaya-w-1026:20200404205346p:plain:w200
岩田さん 岩田聡はこんなことを話していた。

以下のサイトで書籍の一部分が無償公開されているので、気になる人は是非読んでほしい。
www.1101.com

WEB DB Press vol115/vol110

今回の特集は全部興味がある内容だったから全体的にがっつりよんだ。
iOSの開発最近あんまりしてないから参考なったーー🙄
gihyo.jp

こっちはgRPC周りを使う予定だったのでそのために。よくまとまっていた。
ついでに載っていた名前付け大全も良かった。
名前付け関係の話って言われればわかるわかるーって思うんだけど、いざやるとわりと悩むことがむっちゃ多い。むずい。
gihyo.jp

これだからゲームづくりはやめられない!

ゲーム会社で働くエンジニアとデザイナーのお話!
ものづくりのモチベあがるううう!まじで読んでほしいい。
f:id:yaya-w-1026:20200404205633p:plain:w200
これだからゲーム作りはやめられない!(2)

ランウェイで笑って

ただただ二人を見守りたいから買い続けている。最近アニメ化されたーー。
f:id:yaya-w-1026:20200404210744p:plain:w200
ランウェイで笑って(15)

積読📦

ノンデザイナーズ・デザインブック

個人アプリの開発の参考のために!
色んな人からもおすすめだーーってコメント来たけどまだ途中。

f:id:yaya-w-1026:20200404210239p:plain:w200
ノンデザイナーズ・デザインブック

リファクタリング 既存のコードを安全に改善する

いつも自分のためのコードしか書いてこなかった私がバイト中に大反省した暁に購入したやつ。
ちょこちょこ直していきたい部分を読みすすめてる。もうちょっとしっかり読んだら詳しい記事書くかもしれない。
f:id:yaya-w-1026:20200404210900p:plain:w200
リファクタリング 既存のコードを安全に改善する