2024年1月8日月曜日

2024年初頭に思うこと

 10年ぶりに、ここに投稿します。
10年前は、現職に転職したばかりの状態でした。


10年前の私の投稿を見て、変わったところ2割、変わっていないところ8割という感想を持ちました。


変わったところは、主に2つ。

  • 団塊世代の引退などで働き手が減った。さらに、優秀な人材が受託開発から自社サービスに流れている。
  • 公共系、金融系のシステム開発でも、クラウドを検討することが当たり前となった。

変わっていないところは、たくさん...
  • 公共系、金融系などは、10年前と同じプロジェクトマネジメントで進めているところが多い。
  • まだまだ一括請負で億単位の案件を請け負うSIerおよびその下請けは存在する。
  • 優秀な(若手)人材がSIerから流出しているため、大規模一括請負の受託開発の現場で厳しい。

SEという言葉も死語になりつつある。
ただ、相変わらず、技術も理解した上で、お客様やビジネスサイドと話ができる人材は少ない。さらに英語で話ができる、となると希少です。
そして、多くのソフトウェアエンジニアが、お客様やビジネスサイドと話をすることを嫌がる一方、プロジェクトマネージャ・ITコンサルタントと呼ばれる40代50代の人は技術を理解せずに、エンジニアに丸投げする。

仕事をする上で、分業する必要は避けられないが、自分の担当している前後の工程を勉強・理解できる人材が不足している。
特に、日本では労働者が高齢化していて、管理作業がメインとなっている人が増え、生産性が落ちると思われる。

一方、管理者の割合が大きい職場は、金銭的な労働条件が良くても、若者に敬遠されるであろう。
条件が悪い職場で働くソフトウェアエンジニアは、条件のよいところに転職してほしい。
そうしないと、悪い条件、環境で働くのが当然となり、所得も上がらない。

40代以上のエンジニアも、今、話題になっている技術を1つでも少しかじってみて、これから流行するのか、自分なりに考察することが大事ですね。
現状維持は、悪い条件の職場でしか働けなくなります。現状維持は、衰退するだけです。


2014年12月24日水曜日

2014年を振り返って

2014年を振り返ってみると、歴史は繰り返されるというか、予想どおりそうなったなぁと思った。

個人的なキーワードは、「システム連携」「好景気」である。

まず、システム連携について。

クラウドが普及したとはいえ、「システム連携」のノウハウは必要だし、そこにかかるコストは以前と変わらない。10年くらい前のEA・SOAブームのときも同様だ。基幹システムをはじめとした既存システムとの連携ノウハウは、関係部署との進め方含めて一朝一夕で身につくものではない。多くの日本の大企業にとって、ただSaaS含むパッケージソフトを提供したところで、彼らの課題は解決しない。結局、既存システムおよび新規パッケージ双方を熟知したSEが必要となる。こういう連携案件ができるSEは、2014年現在いくらでも仕事あります。単価は高くはないですが、仕事に困ることはありません。そういう意味では、10年前と仕事内容は、全く変わってないんですよね。(単価は下がってますが...)
GUIで連携を設定するEAIツールとかありますが、SaaSとか使っていると、パフォーマンスの制約があったりするので、結局はコーディングした方が、維持管理コストが少なくて済む場合が多い。そういう意味では、AWSはニーズを捉えていると思います。個人的には、システム連携する 担当者は、プログラムを組めるスキルのある人でないと無理だと考えてます。

一方、中小企業は予算がないので、ベンダーからすれば、パッケージを大量に売って利益を稼ぐしかない。まぁ、そもそもシステム連携の必要性がない場合が多いので、それはそれでよいかと思う。


次に好景気について。

リーマンショック前も、SE不足だった。多くの大手企業で大規模(大人数)な案件の開発が進み、デスマーチもたくさんあった。
2014年もその傾向が始まったと感じている。この一年で株価も上がってますし、みずほ銀行、マイナンバーなどの大規模案件の影響で受託開発するSEの人手不足が出ている。
2015年は、このような大規模案件で人がとられる影響もあり、クラウド連携するSE不足もより顕著になると思う。そうなると、程度の差はあれ、連携して出来上がったシステムの品質問題が出始めるのではないか。単にシステム連携が問題なのに、
クラウドはダメだといった論評が出てしまうのではないかと懸念している。
極論かもしれないが、そうならないことを願っています。



2014年10月8日水曜日

曖昧性との...

最近は、技術的なことにあまり時間をとっておらず、プロジェクトマネジメントに関することに時間を割くことが多い。
プロジェクトマネジメントといっても、最近ブームになっているスクラム・アジャイル・リーンいったものではなく、昔からある開発手法(ウォーターフォール)だ。

現在、ITエンジニアは人手不足で、10年前と比較するとシステム受託開発の単価も下がっている。パッケージ導入担当者(コンサルタントという肩書きの人もいる)含めて少人数で効率的にITシステムを構築する必要が出てくる。
パッケージ導入含む受託開発の場合、原価を押さえるための一番の要因は、プロジェクトマネジメントだ。お客様をスムーズにディレクションし、必要最小限のメンバーと開発を進める。少数精鋭でプロダクトを組み合わせたアーキテクチャを提案し、一人あたりの単価を上げるという方法でしか生き残る道はない。

曖昧性とのたたかい―体験的プロジェクトマネジメント論 

曖昧性との共存


この2つの書籍は読んでいて「そうそう。そうだよなぁ。」と納得することが多い。
また、実際に開発に携わっているとき、「ここ注意しないとなぁ」とチェックリストとしても使える。
この本は、あと20年経っても色褪せないだろうなぁ。

2013年12月31日火曜日

2013年を振り返って

今日で、2013年もおしまいです。
自分の関わっている業界で感じたことを中心にこの1年を振り返ります。
今年は、プロダクトの開発に関わるのかなぁ、と思っていましたが、諸事情あり結局SIの1年間となってしまいました。

この1年で、クラウドは普及というより、当然の選択肢となったと感じています。
また、お客様から単一のサービス(やパッケージ)を導入すれば何とかなるレベルのものではなく、様々なサービス(オンプレミスだろうがクラウドだろうが関係ない)を組み合わせるケースが増えてきました。

2014年、確実に言えることは「導入がより複雑になる」ということです。

プログラミングするための設計よりも、様々なサービス・プロダクトを連携した場合の図を描くことができる人がより必要になっているのではないでしょうか。
大手企業であれば、既存のシステムと連携することが必須です。ここでいう連携とは、システム的に連携する必要があるということではなく、システム的に連携する価値があるかどうかという観点からお客様にベストな方式を提案できることが大事です。

特に、連携するサービス間で何からのインシデントが起きたときの設計は、担当者のスキルがもっとも現れるところではないでしょうか。
「一見、サービスインしました」といって、インシデント・オペレーションについて、何も考慮せずにシステム連携した場合の現場の悲惨さは簡単に想像できるでしょう。

また、プロダクト・サービスの淘汰も来年から顕著になってくると思われます。
特に大企業向けのプロダクトは、勝ち組、負け組がはっきりしてくるかと。

以上、2013年年末時点での記録です。
みなさま、2014年もよろしくお願いします。

2013年10月6日日曜日

AWS S3に画像をサムネイル表示するプログラムをscalaで作ってみた

最近は、お客様と打ち合わせすることが多く、コーディングから遠ざかっている。

コーディングしないと、お客様への提案内容(特に技術的に可能かどうか)や見積もりのカンが鈍る。技術は日進月歩で、何もしないとお客様にまともな提案ができない。

今回のS3に画像をサムネイル表示する件も、あるお客様との打ち合わせがきっかけだ。
AWS S3に画像をアップロードしたときに、何でS3にこんな機能ないんだろう?って思った。

当初はscalaまで使ってコード書くつもりはなかったんです。
pythonのs3cmdと、アルバム作成するソフトを組み合わせれば、シェルを組めば実現できると思う。あとrubyを使ってもいいですね。
ただ、アルバム作成ソフトって、IE6に対応してないんです。JavaScriptがもうIE8以上しかダメ。お客様社内の端末はIE6なので、シンプルなHTMLでUIを作った方がいいと思った。(別にJavaScript凝ってもいいけど)

ってことで、早速深夜作業の待ち時間に作成してみた。
環境は以下の通り

  • scala 2.10.2
  • sbt 0.12.3

クラス構成とか考えてません。main関数にベタ書きです。

ソースの説明です。
まず、AWS SDK JavaのAWS S3クライアントを作成する。

//create AmazonS3Client instance
    val s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider);
    val region = Region.getRegion(Regions.AP_NORTHEAST_1);
    s3 setRegion region


次に、AWS S3のバケッド名を設定する。
(ここでは、バケット名をkzsampleとしている。)

//Get Objects in a bucket
    val bucketName = "kzsample";
    val objectListing = s3 listObjects(new ListObjectsRequest withBucketName bucketName)

S3のバケットからJPEG画像のパスをリストで取得する。
(scalaだとここが非常にシンプルなロジックでいいですよね。)

//Set Jpeg Files in jpegList
    val jpegList = new java.util.ArrayList[String]
    objectListing.getObjectSummaries().filter(p => p.getKey().endsWith(".jpg")).foreach(q => jpegList.add(q.getKey()))

VelocityテンプレートにS3から取得したパスを埋め込む。
テンプレートのパスは、templates/order.vmとしています。

//Velocity
    Velocity.init()
    val context = new VelocityContext()
    context put("jpegList", jpegList)
    val sw = new StringWriter()
    Velocity getTemplate("templates/order.vm", "UTF-8") merge(context, sw)

テンプレートのサンプルはこんな感じ。

VelocityでparseしたHTMLをS3にアップロードするオブジェクトに変換する。

val is = new StringInputStream(sw.toString())
    val metadata = new ObjectMetadata()
    metadata setContentEncoding "UTF-8"
    metadata setContentType "text/html"

最後に公開権限をつけて、S3にアップロードする。

val is = new StringInputStream(sw.toString())
//Upload html file to S3
    val putRequest = new PutObjectRequest(bucketName, "html/sample.html", is, metadata)
    putRequest setCannedAcl CannedAccessControlList.PublicRead
    s3 putObject putRequest

ということで、できたサンプルがこれです。

テンプレートのHTMLのデザインをもっと格好よくすれば、応用が効きますね。
HTML5とか....

参考までに、ソースはこちら



2012年9月16日日曜日

playframework1.2のJobでハマったところ

playframework1系は、リクエスト・ジョブ単位でトランザクションを管理している。 シングルトランザクションで業務処理を行う場合は、分かりやすいのだが、マルチトランザクションを用いないと実現できない場合は、かなり面倒なことになる。 
個人的には、playframework2.0系であれば、プログラマが自由にトランザクション範囲を定義することができるため、今後は2.0を使っていきたいが、1系のアプリケーションをメンテする必要がある場合は、マルチトランザクションを実装しないといけない場合があるだろう。 playframework1系でハマったところを備忘録としてメモっておく。 

今回、playframework1系で実現したい機能は以下である。 playframeworkのJob機能を使って、複数のバッチジョブを順に実行したり並行して実行するために、ジョブ制御テーブルをDBにもち、各バッチジョブの開始、終了時にジョブ制御テーブルを更新する。


まずは、playframeworkのJobクラスを拡張する。

トランザクションがネストしていないので、トランザクション境界にはJPAPlugin.startTx(false)とJPAPlugin.closeTx(false)を用いることがポイントだ。

業務ロジック用のJobはコンストラクタに設定してあげればよい。

public class JobInvoker() extends Job{

    /**
     * 業務処理をするJobクラス
     */
    private Job<JobResult> job = null;
    
    /**
     * コンストラクタ、業務処理するJobクラスを設定する。
     * @param job
     */
    public JobInvoker(Job<JobResult> job){
        this.job = job; 
    }    

    /**
     * Jobの実行状況を制御するJobStatusManagerを用いてステータスを変更する。 
     * @see play.jobs.Job#doJob()
     */
    @Override
    public void doJob() throws Exception {
        //この段階で既にトランザクションが開始されている。
        JobStatusManager manager = new JobStatusManager("JobA");
        try {
            
            if (manager.isExecutable()) {
                            //DBのテーブルにあるステータスの値を「実行前」から「実行中」に更新する。
                     manager.startJobStatus();
                     manager.commit();
                            //ここでcloseTxするか、DB.getConnection().commit()しないとDBに値が更新されない。
                     JPAPlugin.closeTx(false);

                            //ここから業務処理用のジョブの開始
                            //業務処理用に新たにトランザクションを開始する。
                            //doTask()メソッドがコンストラクタに設定したJobを呼び出す。
                     { 
                     JPAPlugin.startTx(false);
                            doTask();
                     JPAPlugin.closeTx(false);
                     }

                            //ステータス更新のためのトランザクションを開始する。
                     JPAPlugin.startTx(false);
                            //DBのテーブルにあるステータスを「実行中」から「完了」に更新する。
                     manager.completeJobStatus();
                     manager.commit();
            }

        } catch (Exception e) {
       //異常時はDBのテーブルにあるステータスを「実行中」から「異常終了」に更新する。
            manager.failJobStatus();
            manager.commit();
            Logger.error(e, "%s failed !", this.getClass().getName());

        } finally { 
         manager.close();
      if(JPA.isInsideTransaction()){
             JPAPlugin.closeTx(false);
      }
        }

    }

    /**
     * コンストラクタに設定したジョブを呼び出して結果を取得する。
     * @throws ExecutionException 
     * @throws InterruptedException 
     */
    protected void doTask() throws Exception {
        
        Promise<jobresult> promise = job.now();
        
        if(!promise.get().isSuccess()){
            Logger.error(promise.get().getException(), "Error", null);
            throw promise.get().getException();
        }
    }
}

また、上記のように実装した場合、設定ファイルのapplication.confもいくつか追記しないといけない。


  1. db.pool.maxIdleTimeExcessConnectionsを記述しないとDBのコネクションが解放されずに増え続け、一定時間後にPersistanceExceptionが発生する。
  2. db.pool.minSizeは、play.jobs.poolよりも多く設定しておく。そうしないとコネクションがタイムアウトしましたというPersistanceExceptionが発生する。


#Jobに割り当てる最大スレッド数の設定
play.jobs.pool=10

#DBの設定
db.pool.timeout=10000
db.pool.maxSize=40
db.pool.minSize=20
db.pool.maxIdleTimeExcessConnections=10

やはり仕事で使う場合は、マルチトランザクションは必要だ。また、常駐ジョブは一定期間の安定テストを行う必要がある。playframeworkでマルチトランザクションでバッチを起動するなら上記設定内容だけは覚えておきたい。

2012年8月25日土曜日

クラウド温泉3.0 に参加して(2)

<プログラマのための代数入門2 Monadへの道>

 

中村(@nakayoshix)さんに、Monadを説明していただきました。

といっても1時間でMonadの説明までたどり着くのは無理があるらしく、その前の群の説明が中心でした。特にルービックキューブを使った「逆元」「非可換」の概念は分かりやすかった。

Monadに関しては、日本語のWikiでは、十分な説明がないことが分かりました。

ただ、それをプログラムで実現する場合、IOとか外部の環境を考慮に入れる必要がある。IOなどの環境を包括してくれるモナドがどこまで使い物になるのだろうか。そこは、自分で検証する必要があると感じた1時間でした。

 

<「基礎から見直すTX処理〜A Critique of ANSI SQL Isolation Levelsを再読する」>

@okachimachiorz1 さんの発表。今回のクラウド温泉で最もインパクトが高い発表でした。

トランザクションについて、ANSIのSQL規格ではまだ不十分。理想とするトランザクションがコンピュータで実現された状態を100とすれば、現在はまだ40くらいだそうだ。(根拠はないけど)

私は、ANSIで定義したトランザクション分離レベル(これの理解も中途半端)が常識だと思っていたので、しょっぱなから頭をうたれた。

発表は、「A Critique of ANSI SQL Isolation Levels」を読むための前提知識と左記論文の概要を説明していただきました。

もっとも大事なことは一貫性(consistency)であり、それを実現するため、原子性(atomicity)、独立性(isolation)が存在すると解釈しました。

トランザクション実行後の「何が正しいのか」という基準を明確にする必要があります。

当発表では「isolationはserialisableになればよい」「semantic violationでないこと」を正しいと定義していました。ただ、「serialisable」と「serialised」を誤解する人が多いそうです。(私もそうでした。)「serializable」とは、トランザクションが「serialised」された状態と同じ状態を意味的に最適化したものを意味します。

「serializable」にするために、どうやってrockしたり、snapshotをとったりするのがよいのかまでは理解できませんでした。「Transactional Information Systems」を読めば分かるそうです。(といっても敷居が高い)

ぼちぼち勉強していこうと思っています。

 

<本当は怖いDNSの話>

鈴木(@tss_ontap)さんの発表。

あまり詳しく書くことはできないが、DNSの仕組みが不十分で、あるドメインが乗っ取られることがあった事例を発表していただきました。SSLなどドメインを取得してアプリケーションを構築する場合、セキュリティとしてこんなリスクもあるということを抑える必要があると思いました。

 

その後、小樽商大の教室をお借りして、ディスカッションをしました。

浅海さんの発表をベースに、OOPと関数型を使ったアーキテクチャ、モデリングについて議論しました。業務寄りの部分は今まで通りOOPを使い、基盤よりの部分に関数型を利用すると上手く構築できるのではという内容でした。サーバのコア数が増え、C10K問題を考えると、並列プログラミングをするためには、関数型を使って並列処理が可能な基盤部分のアーキテクチャを構築できる人が重要になってくると実感しました。

自分としても、このような基盤構築に関わっていきたいし、このような基盤を使ったソフトウェア、システムを構築したいと思える2日間でした。今後の仕事には、まずscalaを使ってパイプラインプログラミングが実際に使いモノになるのか検証できたらなぁと思っています。

 

最後に、中村さんをはじめ、発表していただいたみなさま、参加者のみなさま、ありがとうございました。今後ともよろしくお願いします。