One Step Ahead

プログラミングやエンジニアリング全般について書いていきます

キャリアに考える戦略と戦術と戦闘。

はじめに


『戦略による過ちは戦術により補い難く、 戦術による過ちは戦闘により補い難し』

この言葉は、組織運営や経営において使われることの多い言葉ですが、エンジニアとしてのキャリアを考える上でも非常に良い言葉だなと思っています。

昨年、数名の新人さんが退職していきましたが、いずれも異業種からの転職者の方でした。 入職当初、不安と期待が入り混じった表情していたことを思い出すと、何とも言葉にし難いものがあります。

そして、退職時に話していて私の頭に浮かんだ言葉がこの言葉でした。
簡単に言えば,,,

『戦略を間違えばどんなによい戦術を選んでも勝利を掴むことができず、戦術を間違えばどんなに良い戦いをしたとしても勝つのは難しい。』

といった意味です。
今日はこのテーマを元に、退職された方に伝えたかったことについて考えていきたいと思います。

戦略、戦術、戦闘とは?


まずは、言葉の整理です。 いずれも『戦い』という言葉がキーワードになります。
この3つを扱うのは『戦いに勝利する』するためです。
このままでも現代の事柄に当てはめることができると思いますが、より簡単に言えば,,, 『戦いに勝利する』ということは『目的を達成する。』ことだと思っています。

この前提を踏まえた上で、戦略、戦術、戦闘を考えていきます。

一般的には特定の目的を達成するために、長期的視野と複合思考で力や資源を総合的に運用する技術や応用化学
wikipedia ~戦略~

このままだと分かりにくいので、簡単にまとめると,,, 現状に起点を置き、目的を達成するための『期間』と『資源』をどう使っていくのかという『方針』を考えること。 だと私は思っています。

作戦・戦闘において任務達成のために部隊・物資を効果的に配置・移動して戦闘力を運用する術である。そこから派生して言葉としては競技や経済・経営、討論・交渉などの競争における戦い方をも意味するようになる。
wikipedia ~戦術~

これも簡潔にまとめると,,,
戦略に基づく、目的達成のための具体的な手段と方法になります。

戦闘とは、相互に敵対する二つの勢力による暴力の相互作用である。
wikipedia ~戦闘~

言い換えると、戦略に基づいた戦術を実際の『行動』として行うこと。 実際の行動が『戦闘』にあたります。

『現状』を顧みない『戦術』


退職された方に限らず、新人や2年目の方と話していても感じることですが、戦術の基本である『現状を起点とする。』という部分が抜け落ちているように感じます。 『現状を起点とする』という言葉をより具体的にすると、今自分が持っているもの、持っていないものを見極めることだと思っています。

特に『自分が持っているもの』に対する見極めがほぼ出来ていないと感じました。 持っていないものは非常に目につきやすいものです。

『AWSやクラウドの当たり前なのにその知識がない。』
『マイクロサービスアーキテクチャに関する知見がない。』
『DeNAやメルカリみたいな大規模サービスの経験がない。』

情報収集にTwitterなどのSNSを活用する人であれば、有名なエンジニアと自分を比較してしまい、より足りないものばかりが目に付くのかなと思います。 足りないものを追いかけて、SNS上の断片的な情報から神様のようなエンジニアの虚像を作っている人が非常に多いなと感じました。

f:id:EaE:20210108164617p:plain

そして、いつの間にか神様の虚像に追いつくことが、その人の目標になってしまっている方もいました。

自分に足りないものを見つけ、それを真摯に改善していこうとする姿勢は素晴らしいものですが、時間は限られています。 健康や年齢、能力だって人それぞれ違います。『戦略』や『戦術』のすべてを足りないものを得るために使うのは非常に勿体ない。

自分にあるリソースを使って、その中でどういった『戦略』『戦術』が使えるのかということも一緒に考えてもらえればなぁと思います。

一人歩きする『戦術』と『戦闘』


そして、もう一つ感じたのは、『戦術』と『戦闘』の一人歩きです。 『戦術』と『戦闘』を考えて、行動するまではできていますが、フィードバックがない人が多いように感じました。

実行することが目的になってしまい、断片的な善し悪ししか見ていない人が多いと感じます。 『戦闘』の善し悪しを評価するためには、『戦術』が。『戦術』の善し悪しを評価するためには、『戦略』が重要になります。 どこかを部分的にみて善し悪しを判断すると、その部分にだけ良いアクションをとることが目的になってしまうとことが多いと私は思っています。

さいごに


業務をしていると、なかなかこういったことのディスカッションをする機会が持てていないのが現状なので、先行くものとして反省が必要だなと思っています。
『退職していった。』という事実を他人事としてとらえず、一緒に悩めるにようにしていきたい。

新年の挨拶とこれから。

f:id:EaE:20210105102821p:plain
新年あけましておめでとうございます!
前回、ブログを書いたのが2019年とは、時間が経過するのは本当に早いですね...

2020年を振り返ると、世界的にも変化の大きな1年でしたが、個人的にも心境の変化が大きな1年でした。
リモートワークが当たり前に近しい形で導入されるようになり、自分の働き方や今後を考えるよい機会になったかなと思っています。

エンジニアとしてのキャリアを考えることが多かった1年だったなぁと。

内省する時間に比例して、やっと自分のエンジニアとしての自分の軸足みたいなものが定まってきたので、優先すべき事柄の解像度が上がってきたかなと個人的には感じています。

まだ詳しくは言えませんが、2021年は自分としてはなかなかチャレンジングな1年になるかなと今から緊張半分、期待半分といった状態です。

さて、話は変わって今後のこのブログの方向性です...

技術的なことはもちろんですが、エンジニアのライフプラン(主に自分の)的なことを吐露する場所にしていこうかなと思っています。
Qiitaやzenn.devと使ってきましたが、明確に使い分けて行こうかなと思っています。

エビデンスや具体性の高い技術記事に関しては、Qiitaもしくはzenn.devに。
それ以外の、試してみた系や自分の考えを記載するのは、ブログにしようかと思います。

使い分けを決めた理由については、自分の思考を整理する場所としてQiitaやzenn.devを使うのは、他の利用者に迷惑がかかると思ったのが一番の理由ですが、もう一つ理由があります。

キャリアを経るにつれ、InputよりもOutputを求められる量が増えてきているので、自分の中にある考えを明確に『言葉』にして、『表出』させておきたかったというのも大きな理由です。

2021年のやるべき課題の1つとして、更新頻度高めで、やっていきたいと思っております。

フォローしておきたいエンジニア一覧

2019/11/27に勉強会の一環で、エンジニアが活用したい情報リソースまとめというタイトルで、情報の取捨選択方法や、情報収集先一覧を紹介させてもらいました。

その中で、「Twitterでフォローしている人おいた方がいい人はいますか?」という質問を受けたので、自分がフォローしているエンジニアのまとめ (Qiitaにそのまま書いてしまうのは、違う気がしたのでブログにまとめておきます。そして、随時更新されていきます。)

C#

岩永信之さん

恐らくC#で検索すれば真っ先に検索上位にあがるサイト『++C++; // 未確認飛行 C』の管理さんです。
情報収集力の高さにただただ驚かされます。
働き始めてから一番助けてもらったであろうサイトの管理人さんなので、もし会う機会があれば誠心誠意お礼を言いたい。

Atsushi Nakamura

Twitterの紹介文にあるように「持続可能なソフトウェア」についてのお話が非常に面白い方。 はてなブログにも記事を投稿されています。 個人的なオススメ記事は「継続的にテスト可能な設計を考える」「世界一わかりやすいClean Architecture」です。

Go

mattnさん

Go界隈では超有名人だと思います。 個人的にはGoで有名だから知ったというより、Qiitaに投稿されていた「本物の golang を... 本物の Gopher を、お見せしますよ。」の記事をみて単純にファンになってしまいました。

Ruby

まつもとゆきひろさん

言わずと知れた、Ruby言語のお父さんです。 更新頻度は高くありませんが、ブログ?もやっています。
勉強会で何度かお会いしたことがありますが、非常に気さくな方でした。

伊藤淳一さん

チェリー本の愛称で有名な「プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで」の作者です。
Ruby自体は趣味で触る程度ですが、書籍は非常に分かりやすくRuby初学者には絶対に進めたい1冊です。
この本も非常にお世話になったので、会う機会があればお礼を言いたい。

iOS関連

Shuichi Tsutsumiさん

行動力・バイタリティお化けの方です。そのバイタリティの10分の1でもいいから見習いたい。
Tsutsumiさんと比較して、まだまだ頑張れるなと個人的に勇気をもらいました。
(無理を勧めているわけではありませんよ。)

個人開発

TAKUYAさん

MarkdownエディタであるInkdropの開発者です。 個人開発をしている方はブログ(保守していくコツなど)が非常に面白いので一見の価値ありです。

TDD

和田卓人さん

TDDの第一人者の方で、プログラマが知るべき97のことという本の翻訳も行っています。
後は以外と知られていませんが、power-assertテストライブラリの作成者でもあります。
「T-Wadaさんの顔は知らないけど、ライオンは知っている。」と言われるほど、ライオンが一人歩きしています。

その他

Rui Ueyama

Rui Ueyamaさんをどこに分類すればいいかわからず、その他にしてしまいました。
知人から「低レイヤを知りたい人のためのCコンパイラ作成入門」をゴリ押しされて読みましたが、
控え目に言ってものすごく面白いです。個人的には付録もそれだけで記事にできそうなくらい面白いです。書籍化希望です。
(前はオンラインブックがありましたが、今はLinkが切れてしまっていました。)

C#でのPlugin機能の実装

はじめに


前回の「Plugin Architecture」って何だろうか?でまとめを書いたが、それだけでは分かった気になってしまうので、実際に手を動かしてみる。
今回はプログラミング言語の機能としてPluginの実装をするのではなく、アプリケーションレベルで『Plugin機能』を実装していく。

前置き


何はともあれ、コードを見せろ!という方はこちらから。

動作確認環境


  • Windows 10 Pro
  • .NET Core 2.1

アプリケーションの構成


『参考』に載っているアプリケーションはGUIだが、手っ取り早く実装を試してみたかったので、CLIを作成。
読み込んだプラグイン(.dll)の内容に応じて、メニューを表示。対象のメニュー(プラグイン)を選択すると、それに応じた処理を画面に表示する簡単なもの。

インターフェース

拡張点になるインフェースはCLIということで非常にシンプル。

namespace PluginComponent.Plugin
{
    /// <summary>
    /// Plugin Interface
    /// </summary>
    public interface IPlugin
    {
        /// <summary> Plugin name </summary>
        string Name { get; }
        /// <summary> Execute </summary>
        void DoAction();
    }
}

Nameは画面に表示されるメニューで、DoAction()がメニュー選択時の実処理を表している。
プラグイン作成時は、このインターフェースを継承する。
(今回は何も考えずにvoidかつ引数なしにしているが、コアシステムからの入力とコアシステムへの返却のIFも考えておいた方がよい。)

プラグイン

プラグインの実装もかなりシンプル。 名前と実処理を書いてしまえば、実装自体は終了。
プラグインを書いていて思ったのは、拡張点の細分化はやっぱり必須だなということ。
同じインターフェースを使って、『AのプラグインではDBアクセス、Bのプラグインでは..』みたいにやってしまうとコアシステム側で前処理や後処理が煩雑化してしまう。

using static System.Console;
using PluginComponent.Plugin;

namespace FirstPlugin
{
    /// <summary>
    /// FirstPlugin implements IPlugin.
    /// </summary>
    public class FirstPlugin : IPlugin
    {
        /// <summary> Plugin Name </summary>
        public string Name { get; } = "First";
        
        /// <summary> Execute </summary>
        public void DoAction()
        {
            WriteLine("I`m First Plugin.");
        }
    }
}

コアシステム

最も重要になるのは、SamplePlugin/Main/PluginLoader.cs。
こいつが動的に.dll(プラグイン)を読み込み、インスタンスを作成している。

Pluginsフォルダに存在するファイル(拡張子が.dll)のモノを読み込んで、アセンブリをロードする。
アセンブリがロード出来たら、アセンブリのTypeを取得して、interfaceとabstractを除外していく。
そして、pluginsリストにAddする前にIPluginを継承しているか確認を行う。 (ここで上記を除外しておかないと、インスタンス生成時にErrorが発生する。)

対象クラスの選別が出来たら、Activatorを使って、Typeからインスタンスを動的に生成する。
後は生成されたインスタンスをアップキャストして、呼び出し元に返す。

using PluginComponent.Plugin;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Loader;

namespace SamplePlugin.Main
{
    /// <summary>
    /// Plugin Loader
    /// </summary>
    public static class PluginLoader
    {
        /// <summary>
        /// Load Plugin For Plugin directory
        /// </summary>
        /// <param name="path">Plugin directory path</param>
        /// <returns>load assembly</returns>
        public static ICollection<IPlugin> LoadPlugins(string path)
        {
            var plugins = new List<IPlugin>();
            if (Directory.Exists(path))
            {
                var dlls = Directory.GetFiles(path, "*.dll")
                            .Select(p => Path.GetFullPath(p)).ToArray();
                var pluginTypes = new List<Type>();
                foreach(var dll in dlls)
                {
                    var assm = AssemblyLoadContext.Default.LoadFromAssemblyPath(dll);
                    
                    if (assm != null)
                    {
                        var types = assm.GetTypes();
                        foreach(var type in types)
                        {
                            if (type.IsInterface || type.IsAbstract)
                            {
                                continue;
                            }
                            else
                            {
                                // implements IPlugin interface
                                if (type.GetInterface(typeof(IPlugin).FullName) != null)
                                {
                                    pluginTypes.Add(type);
                                }
                            }
                        }
                    }
                }

                pluginTypes.ForEach(pType =>
                {
                    var plugin = (IPlugin)Activator.CreateInstance(pType);
                    plugins.Add(plugin);
                });
                return plugins;
            }
            return plugins;
        }
    }
}

さいごに


この程度であれば、『プラグイン簡単じゃん!』ってなりそうだが、前処理・後処理を考慮した拡張点の細分化、プラグイン入出力のIFの設計はかなり骨が折れそう。
要件を詰めてかつ、実装レベルの設計もある程度理解してないと、実装者が痛い目を見る典型だなという印象。

参考・引用


「Plugin Architecture」って何だろうか?

はじめに


「フロントサイドはPlugin Architectureを意識した実装にしたい.」と言われたが、正直Javascriptはそこまで得意でない。
そもそも、Plugin Archtectureを詳しく知らないと思ったので、明後日の方向に進まぬよう、「Plugin Archtecture」とは何か?自分なりにまとめる。

Plugin Architectureとは?


まず、Plugin Architectureについて。
Plugin Architectureとは拡張性のあるアプリケーションを実現するための設計手法のことを指す。
明確なPlugin Architectureの定義があるわけではなく、「拡張性」をアプリケーションにもたらすために選択される手法全般を指している(※1)。プログラムの特定のポイントで外部のスクリプトを呼び出すスクリプトレベル簡単なものから、インターフェースを定義して使用方法を明確に規定する複雑なものまでいろいろと存在する。
核となる考え方の基本は「アプリケーションのコアとなるシステムが追加対象(componentやmodule)の詳細な実装を知らなくても、既存のアプリケーションに対して機能拡張をすることができる。」という点。

(※1 手法として確立されているものはあるが、Plugin Architectureの主たる目的が拡張性のあるアプリケーションを実現するための設計手法のため、明確なワークフローに従ったものだけをPlugin Architectureと呼ぶものではないと個人的には思った。)

Plugin Architectureで実装するメリット


  • 機能拡張にかかるコストを低減させる

    Plugin Architectureを採用したアプリケーションは、オープンクローズド原則に基づいたアプリケーションであるため、拡張が容易で、変更に強い傾向にある。

  • 第三者に機能拡張を委ねることができる

    Plugin Architectureを採用したアプリケーションは、第三者に対して機能拡張をゆだねることができる。 (コアとなるアプリケーションを修正しなくても、「自分で機能追加していいよ!」というスタンスをとれる。)

Plugin Architectureで実装するデメリット


  • IFやルールが明確でない場合はアプリケーションの保守性が著しく低下する.

    拡張ポイントのIFや拡張機能実装時のルールが明確に定められていない場合、アプリケーションの保守性が低下する。
    ある特定の機能拡張をしたいからといってむやみやたらに、IFを変更すると地獄をみる。

  • 自由度の高いPlugin Architectureは実装難易度が高い.

    自由度が高い(機能拡張の幅が広い)アプリケーションを作ろうとすると実装難易度とセキュリティリスクが跳ね上がる傾向にある。
    「簡易な入力をもつメニューを追加する」などは比較的簡単だが、DB処理の前後でHooKするような拡張機能などは難易度が高くなりやすい。

より良いPlugin Architectureを実現するポイント


  • 1.IFを明確に定める.

    プログラミングレベルで考えると、下記のようになる。
    1. インターフェースを実装するクラスのみが、拡張Component、moduleになりえる.
    2. コールされる拡張点(メソッド名)を統一する.
    3. コアシステムから受け渡される値の型を統一する.
    4. 拡張点(コールされるメソッド)からの戻り値を統一する.

    など。

  • 2.外部コード(Plugin)は本体コードを直接参照しない.

    Pluginが本体コードを直接参照しないようにすることで、不必要な依存関係を持たないようにする。
    セキュリティ的な観点からも、コアシステムのメソッドを簡単にコールできる状態にするのはよろしくない。

  • 3.Plugin同士の依存関係は極力発生しないようにする.

    Pluginが増えてくるとPlugin同士で依存関係を持つことがあるが、テスト容易性が低下するのでオススメできない。 しかし、OSSとして第三者に機能拡張を委ねる場合には、これを止めるすべはない。

  • 4.拡張ポイントを細分化する.

     特定のユースケースを満たすために、むやみやたらに拡張点のIFを変更するようなことはしない。
     拡張可能なポイントを複数容易して、機能別に細分化する。

さいごに


調べてみて、まず思ったことは「これ難しいね。」ということ。 そして、拡張性を望んでいる相手がどの程度の拡張を望んでいるか、探らないことには明後日の方向へ進み、工数がかさむということ。

参考・引用


Powershellのコマンドレット(Cmdlet)と関数(Function)

はじめに


PowerShellを使う上で、何となく腑に落ちない点の最たる例はコマンドレット(Cmdlet)と関数(Function)だと思う。
今回は久しぶりにがっつりとShellを書く機会があったので、ついでに調べてみた。

PS理解に欠かせないコマンドレット(Cmdlet)


コマンドレット(Cmdlet)は、PowerShellで利用可能なコマンドのことで、「Move-Item」などのフルスペル、「md」などのAliasも全て含まれている。
PowerShellの基本(前編) (3/5)
使用可能なコマンドレットは「Get-Command」を実行することで確認できる。

PS C:\Users\User> Get-Command

CommandType     Name                                               Version    Source       
-----------     ----                                               -------    ------       
Alias           Add-ProvisionedAppxPackage                         3.0        Dism         
Alias           Add-ProvisioningPackage                            3.0        Provisioning 
Alias           Add-TrustedProvisioningCertificate                 3.0        Provisioning 
Alias           Apply-WindowsUnattend                              3.0        Dism         
Alias           Disable-PhysicalDiskIndication                     2.0.0.0    Storage      
Alias           Disable-StorageDiagnosticLog                       2.0.0.0    Storage                                                       
Function        Add-BCDataCacheExtension                           1.0.0.0    BranchCache  
Function        Add-BitLockerKeyProtector                          1.0.0.0    BitLocker    
Function        Add-DnsClientNrptRule                              1.0.0.0    DnsClient    
Function        Add-DtcClusterTMMapping                            1.0.0.0    MsDtc       
Cmdlet          Get-DOPercentageMaxBackgroundBandwidth             1.0.0.0    DeliveryOp...
Cmdlet          Get-DOPercentageMaxForegroundBandwidth             1.0.0.0    DeliveryOp...
Cmdlet          Get-Event                                          3.1.0.0    Microsoft....
Cmdlet          Get-EventLog                                       3.1.0.0    Microsoft.... 

ここで主題となる疑問が浮かぶ。"Cmdlet"と"Function"の違いって何?

Cmdlet と Function


結論から言うと、コマンドそのものの成り立ちが違うらしい。
【Cmdlet】

  • コンパイルされたコードで、.NET言語で記述され、.dllから提供される.
  • コンパイル言語により提供されるため低レベルな処理を提供でき、自由度が高い.
  • コンパイルされているため高速に動作する.
  • 作成するときにコンパイルしなければいけない.

【Function】

  • PowerShellで記載されたスクリプトコード.
  • PowerShellで記述するため.NET言語に比較すると自由度が低い.
  • コンパイルする必要性がないため作成が容易.

結局のところ...


開発者側として、"Cmdlet"と"Function"の違いを理解しておくことは有益だと感じた。
ただ、エンドユーザーとして使う上で、これらの違いを知っておく必要性は薄い印象。普段の会話の中で、「コマンドレット」という場合には、明確にこれらの違いを意識する必要もさせる必要もなく、単に"PowerShellで利用可能なコマンドのこと"という理解の方が無用な混乱を招かない。

参考・引用


proxy配下でのyarn実行

はじめに


proxy配下でyarnを実行しようとして、速攻で躓いたのでそのまとめ。

設定確認


まずは設定情報を確認する。

yarn config list

このコマンドを実行することで、設定情報の一覧を確認することができる。
設定情報の中にproxyもしくはhttps-proxyの設定情報が存在するか確認する。
設定が無ければ、ここに設定を追加する。

proxy設定の追加


yarnの設定情報に対する操作はyarn configのサイトで確認できる。
設定の追加方法が確認出来たら、後は設定を追加するだけ。

// ID & Passなし
yarn config set proxy http://example.com:port -g

// ID & Passあり
yarn config set proxy http://username:password@host:port -g

httpsの設定も必要であれば、下記の要領で設定できる。

// ID & Passなし
yarn config set https-proxy http://example.com:port -g

// ID & Passあり
yarn config set https-proxy http://username:password@host:port -g

後はyarn addをしてみて、installできるか確認するだけ。