LoginSignup
7
3

More than 5 years have passed since last update.

ベンチ要員から見た技術的負債

Posted at

Androidその2 Advent Calendar 2016 - Qiita 17日目の記事です。

Android アプリ開発

平素よりお世話になっております。
Android アプリの開発では色々なプロジェクトを横断したベンチ要員役をすることが多いのですが、ベンチ要員から見るスタメン選手の大変さについて改めて考えると、ベンチを温める身ではありますが、経験からスタメン枠の負担をうまく減らせるのではないか?と思っています。
そんなベンチ要員の Android アプリ開発で見聞した何かをここに吐露していこうかと思います。

ベンチ要員って?

いろいろな定義があると思うのですが、自分が思うベンチ要員とは 「スタメンの誰かに怪我や病気のアクシデントや、累積イエローの出場停止や、他のリーグや国際試合などで試合に出られない場合に、代わりにポジション/職務を受け持つ係」 のことだと思います。
他に「2番手」だったり「サブ」などの呼び方はありますが。時には事情によりスタメン枠が増加してしまうため、急遽先発で出場することもあります。アプリ開発において通常は9人制だったとしても、スタメン枠が20人になることなんて別に珍しくはありません。ルールは生き物です。
ベンチ要員は 「サブになるチャンスも皆無で、お呼びがかかることもない万年ベンチにいるベンチウォーマー」 という意味でも使われていると思いますが、それはあまりに悲しいので今回は前述の意味で使っていきます。

スタメン枠は、定期的な人の入れ替わりはありつつもチームの連携を高めるために、メンバーは固定化されていることが多いので、ベンチ要員としてプロジェクトに参加するときもスタメン枠の連携や、空気、所作などの文化を壊さないように注意して動くようにしています。
とはいえ、そのプロジェクトのスタメンを狙うサブのポジションではありません。所詮はスポットでいっときん戦力になる以外はベンチで静かに暮らすのがお仕事なので、自分が活躍する機会が多くないほうが良いに決まっています。

ベンチを暖めるための準備

野球やサッカーなどのスポーツと違って、アプリ開発のベンチ要員は多くの作業を求められます。一番多いのは炎上してる現場に急行してスタメン選手と同じように鎮火作業を行うわけですが炎上しているプロジェクトに安易に人員追加をしても余計に場が混乱するだけなので、ベンチ要員に任命されたからには、自身が関わるプロジェクト全てに常にアンテナを張り続けるようにしていたと思います。呼び出されないほうが良いですが呼び出されたら、すぐに動けるように準備しておくのも
炎上案件の多くは色々な不備からくることが多いように思います。人員追加は単純作業には大変強くなる反面、人員管理のコストが増加や、ソフトウェアのソースコードの品質を維持するのが難しくなるなどの問題のほうが多いように思います。
Wikipedia を調べてみましたが、こういうのは Software crisis /ソフトウェア危機 と呼ぶようですね。
初出が 1968年の第1回NATOソフトウェア工学会議だと言うのですから実に50年近くに渡り、形を変え今もなお定期的に起きているようです。

ソフトェア危機の原因 -- とは

ソフトウェア危機の原因は、ソフトウェア開発工程の全体としての複雑性と専門分野としてのソフトウェア工学の相対的な未熟さと密接に関連していた。ソフトウェア危機は以下のような形で実際の開発プロジェクトに現れた。
・予算を超過してしまったプロジェクト
・予定期間を超過してしまったプロジェクト
・品質の低いソフトウェア
・要求仕様を満たさないソフトウェア
・管理不能状態のプロジェクトと、保守困難となったコード
相反する要求は常にソフトウェアの開発過程を妨げてきた。例えば、ユーザーは多大な機能を要求するが、顧客はソフトウェアに支払う対価と開発期間をなるべく最小にしたがることが多い。

なるほど。確かに。

実際の開発現場を考えるとスタメン選手がプロジェクトを健全に運営しているように見えても、顕在化していないものも含めると大小問わず常に何かしらの問題が起きていたように思います。

ベンチ要員の日常

プロジェクトのコアメンバーではないベンチ要員は常に「いつ声がかかるのか?」という緊張感を持ち続け、呼ばれたら直ぐに全力で動けるよう準備は怠りません。ミーティングやチーム勉強会などにも同じように出るし、ただ聞くだけではなく発表もします。馴れ合う必要はありませんが互いを知る上で、チームの文化、プロジェクト内の暗黙的なルールを知ることも含めコミュニケーションは必要不可欠です。

とはいえ、実際のプロジェクトのスタメンメンバーは日々の機能開発やリファクタリングなどチケットの消化に忙しく、外様な人間など 会話できる石ころ 程度の存在価値しかないかもしれませんが、そこは大人です。良い仕事をする上では誰に対してもリスペクトを忘れてはいけません。賞賛には拍手を送り、悪しき行為、愚かな振る舞いには罵声を浴びせます。

とはいえ、やはり「求められるもの」が違うので普段の作業は当然違ってきます。

  • ボール拾い(こぼれた問題や、喉に刺さる魚の小骨のような問題のキャッチアップ)
  • パフォーマンス測定(コードの品質が悪い箇所を指摘したり、設計的な問題や内在する問題を可視化する)
  • 番外レビュアー(色々なプロジェクトに絡んでいるとコードレビューの前のコードレビューなんてのも)
  • 相談うなずき係(「設計を変えたい、クラス設計とは云々、この機能のためにOSSを...」みたいな技術的な相談をされるんですけど、ほとんどの場合、自分の中で結論が出てる人が多いので、よほど無茶苦茶でないかぎり頷いて同意するだけ)
  • 後片付け(たまに涙なしには語れない事情で Eclipse で開発している方々もいるので AS に移行できるように現場監督に交渉する。ベンチ要員なら偉い人への直訴をやっても波風が立ちにくいこともある)
  • 刺客(実際にはこれが一番多かった。技術や設計の刷新には血の涙が流れるのです)

何から何まで不思議

プロジェクトには色々なルールがあって、どこでも同じような大局的なルールから、プロジェクトごとのローカルルールまで多岐にわたり、何から何まで全て同じということは少なかったと思います。
特にコードレビューのような作業をしていると色々と不思議なコードに出会うこともありました。それはパッと見て ??? と思うような内容であったとしても、さながらワンピースのポーネグリフのように異様な佇まいで誰も手が出せない聖域となりレビューからもリファクタリングの対象にもならない力をまとっています。
残念ながらベンチ要員ごときでは、そのようなものに出くわした時にはあまりに無力ですが issue に登録するかチケットに残すぐらいしかできなかったこともありました。

どうしてこうなった?

自分が出会った謎のコードについて。バグに不可能はないといいますが、バグではなくとも不思議な物はたくさんあって、記憶にあるものを少し紹介したいと思います。

謎の変数たち

この Flag や Mask は継承される運命から逃れられない

// BaseHoge.java
public BaseHoge {
    public final int FLAG = 0x0001; // FLAG
    public final int MASK = 0x0001; // MASK
    ...
}

// HomeHoge.java
public HomeHoge extends BaseHoge {
   ...
   // FLAG も MASK も使ってない
}

// SettingHoge.java
SettingHoge extends BaseHoge {
   ...
   // FLAG も MASK も使ってない
}


/* 
以降も特に使ってないけど BaseHoge を継承することで色々な箇所に永遠と残る FLAG さんと MASK くん。 
きっとメモリを覗くと常にいるんだろうけど、もう何がしたいのかさっぱり分からない。昨日、今日ではなく年単位で残る謎の定数。
*/

先ほどとは不思議さとは別の方向に不思議な変数。

public BaseHoge {
    // どこを見ても他のクラスから参照されないのに public で static な FLAG & MASK コンビ
    public static final String FLAG = "FLAG";
    public static final String MASK = "MASK";
}

boolean じゃダメだったかのぉ...

class Hoge {

    private int isFlag = 0;

    public void enable() {
        isFlag = 1;
    }

    public void disable() {
        isFlag = 0;
    }

    public boolean isEnable() {
        return isFlag == 1;
    }
}

謎のクラス・メソッド

Hoge は Singleton ではないのに?

public Hoge {

     // コンストラクタ
     public Hoge() {
       // 空っぽ
     }

     // Hoge の Instance を生成するだけのメソッド
     public Hoge getInstance() {
          return new Hoge();
     }
}

AppCompatActivity まで使えるようにしながらも、あくまで SupportLib には頼らず「Activity でいく!」という強い主張なのかもしれません。

// build.gradle
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:support-v4:23.4.0'
    compile 'com.android.support:design:23.4.0'
    ...


// Hoge.java
import android.app.Activity;
...

// AppCompatActivity の方じゃなくて???
public class HogeActivity extends Activity {
    ...
}

そうだよね、手段は豊富な方が良いよね。
でも、そんなに念入りに準備されても。

// Hoge.java
public class Hoge {
    public static intent getCreateIntent() {
        Intent intent = new Intent(this, Hoge.class);
        return intent;
    }
}

// HogeUtil.java
public class HogeUtil {
   public static intent createIntent(Class clazz) {
        Intent intent = new Intent(this, clazz);
        return intent;
   }
}

世の中の皆様的にはわかりませんが個人的には Flagment を View と名付けるのは控えていただけると大変助かります m(_ _)m

public class HogeCustomView extends Fragment {
   ...
}

この「名前を付け替えちゃう系」は実際にはとても多く、色々なところで見かけたと思います。特に多かったなぁ感じたのはボタンでした。

class RegisterButton extends TextView {...}
class RegisterButton extends ImageView {...}

悪いとはいいませんが、そもそも Button は TextView を継承しているので素直に Button でよかったんじゃ?みたいなこともあったか、無かったのかわかりません。

スクリーンショット 2016-12-16 22.30.44.png

ここまであげた例は実際のコードとは少し違いますが、だいたい似ていると思います。
もちろん、このコードが跋扈しているのは理由は必ず存在します。スタメンは機能開発とバグ修正が山積みでリファクタリングの時間がないこともあれば、複雑に継承されるクラスであれば一部でも変更してしまえば色々な場所に波及してしまうため「本当に簡単な修正で済むのか?」という疑念を払拭するための「調査作業」だけでも多くの時間を要してしまいます。

より高度なものは何かの勉強会のネタで使ってから記載したいと思います。

Layout.xml にまつわる話

レイアウト関係についても色々なものを見てきたんですが、ソースコードに比べてレビューもざっくりで「動けば良いんだろ?」みたいなことも少なくありません。

たまに LinearLayout や RelativeLayout を Java コードで inflate することもありますが、頻度が高すぎるとちょっと閉口してしまいます。

    // 確かに動くけどレイアウトの変更をするのに Layout.xml とコードの両方を往復するのは面倒くさい
    LinearLayout ll = new LinearLayout(this);
    ll.setOrientation(LinearLayout.VERTICAL);

    for( int i = 0; i < labels.size(); i++ ) {
        Button b = new Button(this);
        b.setText(labels.getString(i));
        b.setLayoutParams(new LinearLayout.LayoutParams(
            LinerLayout.LayoutParams.MATCH_PARENT,
            LinerLayout.LayoutParams.WRAP_CONTENT));
        ll.addView(button1);
    }
    ...

レイアウトで抱える問題はコードの複雑性や多様性に比べると楽なことが多く、だいたいが「分割しすぎて include が激増し修正するためのレイアウトを探すのが大変」とか「複雑なレイアウトを実現しようと頑張りすぎて5段7段は当たり前のようなネストの深さ」が機能開発の足かせになっていたことが多かったように思います。
(自分の周りだけかもしれないですが)「CoordinatorLayout が〜」みたいな話を除けば、大抵は PercentRelativeLayoutFlexbox-Layout を導入することで問題の複雑さが解決されることが多かったと思います。
PercentRelativeLayout も良いんですけど Flexbox-Layout はサンプルが素晴らしく、「こんなことができますよ」という動きを説明するにしても、github の animation gif を見せると言葉でする説明が半分ぐらいはすっ飛ばせることが多かったように思います。

こちらについても、より高度なネタについては何かの発表で使ってから記載したいと思います。

終わりに

自分の経験では酷いコードは書いた人間の技量や知識の問題では無かったことが多かったように思います。
そうさせる環境やワークフロー。良かれと思って作ったルールに対して、時間が経過するにつれてルール自体を神格化してルールに違反することを厳罰化してしまう意識。そのような環境から生まれる集団思考などメンタル的に余裕のない状況から生まれるものが多いと思いました。
一度、思考停止のループに入ってしまうと、どんなに技術的負債がたまっていても「他所は他所。うちはうち」「今動いているのだから、そこに手を加える必要があるのか?」という思考に落ち込んで、ずっと目の前にあったはずの問題に蓋をしてしまい、とにかく目の前に積まれた機能開発をこなすことだけが全てになってしまいがちです。

再びベンチ要員として呼ばれることがあれば、今度は刺客の役回りは少なめで過ごしたいと思う次第です。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3