インフラ新卒研修と社内ISUCONのはなし

インフラ部の荒井(@ryot_a_rai)です。

今年の4月、弊社には11名の新卒エンジニアが入社しました。そして現在、3ヶ月間の研修を受けています。ビジネスマナーから技術研修まで幅広く行われていますが、その中で5月下旬におこなったインフラ研修とその後の社内ISUCONについてご紹介します。

インフラ研修(講義)

f:id:ryotarai:20160610152008j:plain

インフラ研修はインフラ部に配属されるエンジニアに限らず、全新卒エンジニアが参加する研修です。日常業務でコードを書いてサービスを開発していくうえで知っておいてほしい、インフラに関する基礎知識や共通言語を獲得することを目的としています。合計3日間をインフラ部の@kani_bと分担して講義しました。研修内容の内容はざっくりと以下のようなものです。

1日目

  • インターネットとは
    • ブラウザでウェブサイトを閲覧する際になにが起きているのか
    • IPからHTTPまでざっくりと
  • Webインフラアーキテクチャ概観
    • 三層アーキテクチャ(Webサーバ層、Webアプリーケーション層、データベース層)の話
    • スケールイン/アウト
  • Vim超入門
  • アプリケーションサーバ
    • Rack, Ruby on Railsの仕組み、役割
    • WEBrickやUnicornなどのHTTPサーバの話

2日目

  • データストア
    • MySQL
      • スロークエリ, explain, インデックス
      • トランザクション, ロック
      • レプリケーションとスケールアウト
    • Memcached
      • slab allocator, consistent hashingなど使う上で知っておきたい知識
    • Redis
      • Memcachedに比べた利点や使いどころについて
    • 全文検索
      • Solr, Elasticsearchの役割や利点について
  • Webサーバとプロキシ
    • nginx
      • 静的ファイルの配信
      • Unicornなどアプリケーションサーバの前段に配置する意味

3日目

  • その他の構成要素
    • キャッシュ
      • Railsキャッシュストア
      • Varnishによるレスポンスのキャッシュ
    • CDN
      • CDNの役割や利点について
    • バッチ処理
      • バッチ処理とはなにか
      • 弊社のバッチ処理環境について
      • バッチを書く際に気をつけるべきこと
  • Infrastructure as Code
    • なぜコードで記述するか
    • Itamae, Serverspecによるサーバプロビジョニング
    • これまでの手作業を自動化してみる
  • いわゆる”クラウド”について
    • AWSなどいわゆるクラウドサービスの利点と各種サービスについて

全体として自分で触って覚えられるようにハンズオンを多くおこないました。ひとりひとりVirtualBox( + Vagrant)でVMを構築し、その中で演習をできるようにしています。例えば、自分で実装したRackアプリケーションをWEBrickやUnicornで動かしたり、nginxを前段に挟んでベンチマークを取って効果を確認したりしました。

段階グランプリ(社内ISUCON)

4日目は新卒研修のフィナーレとして段階グランプリ(社内ISUCON)を開催しました。ISUCONは年に一回開催されているパフォーマンスチューニングコンテストで、段階グランプリはその社内版です*1。段階グランプリは新卒研修の一環ですが、せっかくなら新卒以外の社員にも参加してもらおう、ということでエンジニア全体で参加者を募り開催しました。参加者は新卒11名(4チーム)、新卒以外23名(9チーム)となり大盛況でした。

準備

今回の段階グランプリの準備・運営はインフラ部の3名(@mirakui, @kani_b, 私 @ryot_a_rai)でおこないました。具体的には以下の準備をしました。

  • 参加者ポータルサイト(mirakui)
    • ベンチマークを実行したり、ベンチマーク結果を見たり
    • 社内の参加者以外からも見えるようにして、お気に入りのチームを応援できるようにしました
    • 素敵なドメイン(段階.jp)でアクセスできるようにしました
  • 参考実装(ryot_a_rai)
    • 複数言語実装は用意せず、Ruby on Railsでの実装のみを用意しました(弊社で最も多く利用されている言語・フレームワーク)
    • 新卒研修で学んだことを活かせるよう、N+1クエリ、スロークエリなど日常でお目にかかるような消耗ポイントを用意しました
    • テーマは「雑実装なクックパッド」でした
  • ベンチマーク(ryot_a_rai)
  • サーバの準備(kani_b)
    • 参加者の環境やベンチマーカなどを用意
    • 今回はAWS EC2上で1チームにつきc4.largeを3台(io1 EBS 100IOPS)を用意しました
      • gp2を使っていないのはバーストを防ぐため
    • メインのAWSアカウントとは別のアカウントを利用していたため、インスタンス数の制限に引っかかって、急いで緩和申請を上げました…

余談ですが、新卒研修準備、社内ISUCONの開発合宿に利用したヴィラージュ伊豆高原がよかったです。会議室や部屋によっては大きめのテーブルがあり夜中まで開発をやっていけますし、いい感じの温泉がありました。

結果

f:id:ryotarai:20160610151941p:plain

最終結果は上のようになりました。本家ISUCON本選出場勢(@sora_h, @eagletmt)が大人げないスコアで優勝しましたが、新卒チームも初期スコアの4, 5倍のスコアを出し、新卒研修の成果が見てとれました。参加者の感想もおおむね好評で今後も定期的に開催していきたいと考えています。

まとめ

以上、今年のインフラ研修についてご紹介しました。今年は講義形式や社内ISUCONが初回だったこともあり、準備も大変でしたが、今年の経験を来年以降にも繋げられるといいと思っています。こんな新卒研修や社内ISUCONに参加したい、主催したいというあなた、ぜひ一緒にやっていきましょう!

*1:本家ISUCONについてはこちら

API クライアントを書きつつ Swift らしいコードを考える

こんにちは、技術部モバイル基盤グループの茂呂(@slightair)です。

クックパッドは Garage という RESTful Web API 開発を楽にする Rails のためのライブラリを作り、内部通信やモバイルアプリケーションのためのAPIサーバの開発に利用しています。

過去の Garage の紹介記事はこちらです。

この Garage を使って実装された Web API を iOS アプリから気軽に呼べるように、 Swift で Garage のクライアントを実装してみました。

この記事では、GarageClientSwift の紹介をしつつ、これを作りながら Swift らしいコードってどんなコードなんだろうと考えたことをつらつらと書いていきたいと思います。

Garage

Garage は RESTful Web API 開発のためのライブラリです。OSSとして公開しています。 https://github.com/cookpad/garage

今回はクライアントサイドの話をしたいので Garage 自体の説明は過去の記事にまかせます。

記事で紹介されているサンプル実装を使ってクライアントの開発・動作確認を行います。 手元で動作を確認しながら読みたい場合は、リポジトリからコードをチェックアウトして動かしてください。 https://github.com/taiki45/garage-example

クライアントアプリケーションの動作確認時には、サーバアプリケーションのアクセストークンが必要になるので、過去の記事の手順にしたがって取得してください。

GarageClientSwift

GarageClientSwift はその名の通り、GarageClient の Swift による実装です。 https://github.com/slightair/GarageClientSwift

GarageClientSwift は僕が趣味でなんとなく書いたものなので、クックパッドのアプリでもうバッチリ使っているぜ!…というわけではありません。ただ基本的な機能はそろっているのではないかと思います。

GarageClientSwift は HimotokiAPIKit というSwiftのライブラリに依存しています。 これらのライブラリについては後述します。

GarageClientSwift の使い方

GarageClientSwift は Carthage でプロジェクトに導入できます。 詳しくは README.md を読んでください。 この記事では GarageClientSwift 1.1.0 の実装を使った例を出します。

GarageClientSwift の workspace に Demo.playground を同梱しているので、コードを触りながら動作を確認したければこれを利用できるでしょう。 Demo.playground を動かす際は一度 GarageClient iOS の scheme でビルドしてから playground ファイルを開いてください。

この節で説明するものは、この playground ファイルに記述されているものです。

リソースのモデルを定義する

Web API Client を使うということは、なんらかのリソースを取得したいと考えているはずです。 ここでは User リソースを取得することを考えます。 以下のように User 構造体を定義します。 リソースモデルは Himotoki の Decodable に準拠するようにします。

struct User: Decodable {
    let id: Int
    let name: String
    let email: String

    static func decode(e: Extractor) throws -> User {
        return try User(
            id: e <| "id",
            name: e <| "name",
            email: e <| "email"
        )
    }
}

リクエストを定義する

次にリソースを得るためにどのようなリクエストを投げるか定義します。 /users に GET リクエストを送信してユーザーの一覧を取得しましょう。 このようなリクエストを表現する構造体を定義します。

struct GetUsersRequest: GarageRequestType {
    typealias Resource = [User]

    var method: HTTPMethod {
        return .GET
    }

    var path: String {
        return "/users"
    }

    var queryParameters: [String: AnyObject]? {
        return [
            "per_page": 1,
            "page": 2,
        ]
    }
}

なんとなくやりたいことがわかると思います。 APIKitを知っている人はそのまんまだと感じていると思います。

Garage の設定を定義する

次にGarageアプリケーションへ接続するための情報を用意します。 GarageConfigurationType というプロトコルがあるので、それに準拠する構造体かクラスを定義してそのインスタンスを作ります。ここでは単純にGarageアプリケーションのベースURLとアクセストークンをただ保持している構造体を作りました。実際にはアクセストークンを認可サーバから取得してそれを返してくれるような認証・認可機能を実装したクラスになると思います。

struct Configuration: GarageConfigurationType {
    let endpoint: NSURL
    let accessToken: String
}

let configuration = Configuration(
    endpoint: NSURL(string: "http://localhost:3000")!,
    accessToken: "YOUR ACCESS TOKEN"
)

リクエストを送信する

あとはリクエストを送信するだけです。 GarageClient のインスタンスを作って、sendRequest メソッドでリクエストを送信します。 リクエストのコールバックには Result.Success.Failure が引数に渡されるので結果に応じた処理を記述します。 .Success の場合には、取得したリソースやページングのための件数などの情報を含む GarageResponse 構造体を取得できます。

let garageClient = GarageClient(configuration: configuration)
garageClient.sendRequest(GetUserRequest()) { result in
    switch result {
    case .Success(let response):
        debugPrint(response)

        let users = response.resource
        debugPrint(users)
    case .Failure(let error):
        debugPrint(error)
    }
}

以上が GarageClientSwift を使ったリクエスト送信までの流れです。

Himotoki

Himotoki はJSONをデコードしてモデルにマッピングするためのライブラリです。 https://github.com/ikesyo/Himotoki

この記事では Himotoki 2.0.1 の実装を使った例を出します。

Himotoki を使って以下の様な JSON を User構造体にマッピングするにはこのように記述します。

JSON

{
  "id": 2,
  "name": "bob",
  "email": "bob@example.com"
}

User.swift

struct User: Decodable {
    let id: Int
    let name: String
    let email: String

    static func decode(e: Extractor) throws -> User {
        return try User(
            id: e <| "id",
            name: e <| "name",
            email: e <| "email"
        )
    }
}

e <| "id" のような見慣れない構文が登場しますが、これは Himotoki の Extractor のためのオペレータです。JSONから指定したキーの要素を期待通りの型で取り出すための工夫です。

Himotoki の実装をのぞいてみる

<| はどのような実装になっているのか見てみましょう。

https://github.com/ikesyo/Himotoki/blob/2.0.1/Sources/Operators.swift

infix operator <| { associativity left precedence 150 }

/// - Throws: DecodeError or an arbitrary ErrorType
public func <| <T: Decodable>(e: Extractor, keyPath: KeyPath) throws -> T {
    return try e.value(keyPath)
}

Swift ではオペレータを定義することができるので、その結合の仕方と優先度、処理を定義しています。 <| は Extractor の e.value(keyPath) を呼んでいることがわかりました。

https://github.com/ikesyo/Himotoki/blob/2.0.1/Sources/Extractor.swift

private func _rawValue(keyPath: KeyPath) throws -> AnyJSON? {
    guard isDictionary else {
        throw typeMismatch("Dictionary", actual: rawValue, keyPath: keyPath)
    }

    let components = ArraySlice(keyPath.components)
    return valueFor(components, rawValue)
}

/// - Throws: DecodeError or an arbitrary ErrorType
public func value<T: Decodable>(keyPath: KeyPath) throws -> T {
    guard let rawValue = try _rawValue(keyPath) else {
        throw DecodeError.MissingKeyPath(keyPath)
    }

    do {
        return try T.decodeValue(rawValue)
    } catch let DecodeError.MissingKeyPath(missing) {
        throw DecodeError.MissingKeyPath(keyPath + missing)
    } catch let DecodeError.TypeMismatch(expected, actual, mismatched) {
        throw DecodeError.TypeMismatch(expected: expected, actual: actual, keyPath: keyPath + mismatched)
    }
}

value メソッドはつまり、与えられた keyPath で Dictionary から要素を取り出し、返り値の型の decodeValue を呼びだして値を返しています。TypeConstraints を使って Decodable プロトコルに準拠していることを制限に課しているので、e <| "id"の返り値が Decodable に準拠する型でないといけません。

上記のJSONの "id" 要素は数値なので Int になることを期待します。Int や String のようなよく使う型に対しては Himotoki ですでに Decodable に準拠するための実装が extension で追加されています。 https://github.com/ikesyo/Himotoki/blob/2.0.1/Sources/StandardLib.swift

さて、User 構造体の decode メソッドは以下のように実装していました

static func decode(e: Extractor) throws -> User {
    return try User(
        id: e <| "id",
        name: e <| "name",
        email: e <| "email"
    )
}

User 構造体では、 id は Int、name と email は String と型宣言してあるので、コンパイラが e <| "id"Inte <| "name"String が返ると推論します。型推論がうまく働いてくれるのですっきりした記述になるわけです。

Decodable プロトコルはどのような定義になっているのでしょうか。 https://github.com/ikesyo/Himotoki/blob/2.0.1/Sources/Decodable.swift

static func decode(e: Extractor) throws -> Self を定義していることを要求しています。 これが、先ほど見つけた decodeValue メソッドから呼ばれます。

Swift の Protocol にはデフォルト実装を Protocol extension で追加できます(Swift2 から) なので Decodable に準拠している構造体は decodeValue メソッドを実装していなくてもデフォルトの実装が使われます。

Himotoki の Decodable プロトコルに準拠していれば、JSON から作られたDictionaryを以下のようにしてモデルにマッピングできます。

let user: User? = try? decodeValue(JSON)
let users: [User]? = try? decodeArray(ArrayJSON)

便利ですね! 期待した型とJSONの要素の型が一致しない場合は例外が投げられマッピングに失敗します。

Himotoki は Generics と型推論、Protocol をうまく使った例だと思います。

APIKit

APIKit はリクエストとレスポンスを抽象的に表現できて使いやすいAPIクライアントライブラリです。 https://github.com/ishkawa/APIKit/

リクエストを表す構造体を定義し、それに対応するレスポンスをモデルで受け取れるのが特長です。 リクエストに渡すパラメータの型を明示できます。 リクエスト結果は、成功と失敗のどちらかの状態を表現する Result 型で受け取れます。Result には成功時に目的のオブジェクトを、失敗時にエラー情報を含めることができるので、Optional な変数を用いることなくリクエスト結果を受け取ることができます。

この記事では APIKit 2.0.1 の実装を使った例を出します。

使い方を見てみましょう。 https://github.com/ishkawa/APIKit/blob/2.0.1/Documentation/GettingStarted.md

まずはリクエストを定義します。 サンプルは GitHub API の Ratelimit を取得する API を実行するようです。 RequestType プロトコルに準拠した RateLimitRequest とそのレスポンスを表すモデル RateLimit を定義します。

struct RateLimitRequest: RequestType {
    typealias Response = RateLimit

    var baseURL: NSURL {
        return NSURL(string: "https://api.github.com")!
    }

    var method: HTTPMethod {
        return .GET
    }

    var path: String {
        return "/rate_limit"
    }

    func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response {
        guard let dictionary = object as? [String: AnyObject],
            let rateLimit = RateLimit(dictionary: dictionary) else {
                throw ResponseError.UnexpectedObject(object)
        }

        return rateLimit
    }
}

struct RateLimit {
    let limit: Int
    let remaining: Int

    init?(dictionary: [String: AnyObject]) {
        guard let limit = dictionary["rate"]?["limit"] as? Int,
            let remaining = dictionary["rate"]?["limit"] as? Int else {
                return nil
        }

        self.limit = limit
        self.remaining = remaining
    }
}

RateLimitRequest 構造体には API の baseURLmethodpath などのリクエストを構築するために必要な情報を記述します。 また、レスポンスをどのようにモデルにマッピングするかを responseFromObject メソッドに記述します。

リクエストの定義ができたらそれを使ってリクエストを投げます。 コールバックには Result<T, Error> が渡されるのでそれに応じた処理を記述します。 .Successの場合はレスポンスをマッピングしたモデルが含まれているので、後は好きなように扱えばよいでしょう。 RateLimitRequestのレスポンスはRateLimitと定義してあるので、resultResult<RateLimit, Error> であり、.Success<RateLimit> が渡されるわけです。なので limitremaining のプロパティにアクセスできます。

let request = RateLimitRequest()

Session.sendRequest(request) { result in
    switch result {
    case .Success(let rateLimit):
        print("limit: \(rateLimit.limit)")
        print("remaining: \(rateLimit.remaining)")

    case .Failure(let error):
        print("error: \(error)")
    }
}

APIKit の実装をのぞいてみる

APIKit の RequestType プロトコルの実装を見てみましょう。 https://github.com/ishkawa/APIKit/blob/2.0.1/Sources/RequestType.swift

RequestType はリクエストを表現する構造体が準拠すべきプロトコルでした。 baseURLmethodpathqueryParametersheaderFields などなど様々なプロパティがありますがほとんどにデフォルト実装が用意されており、オプションのパラメータはリクエストを定義する際に指定したいものだけ実装すれば良いようになっています。

受け取ったレスポンスをパースしたオブジェクトをどのようにモデルにマッピングするかを以下のメソッドに記述します。デフォルトではレスポンスに JSON を期待しています。dataParser プロパティを指定すれば JSON 以外も受け付けることができます。

func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response

他にも以下の様なメソッドが宣言されています。

func interceptURLRequest(URLRequest: NSMutableURLRequest) throws -> NSMutableURLRequest
func interceptObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> AnyObject

これらのメソッドをリクエストの構造体に実装することで送信する URLRequest に追加の情報を付与したり、レスポンスに応じて独自のエラーを投げてエラーレスポンスを処理することができるようになっています。 これらのメソッドも必要でなければデフォルト実装が利用されるので定義を省略することができます。

APIKit は Swift の Protocol をうまく利用していると思います。

次に Session を見てみましょう。 https://github.com/ishkawa/APIKit/blob/2.0.1/Sources/Session.swift

Singleton の Session オブジェクトを持っているので、通常の利用範囲であればクラスメソッドの Session.sendRequest メソッドを使えば良いようになっていることがわかります。

sendRequest メソッドは TypeConstraints で引数 request の型 RequestRequestType に準拠しているべきと制約を課しています。

public func sendRequest<Request: RequestType>(request: Request,
                                        callbackQueue: CallbackQueue? = nil,
                                              handler: (Result<Request.Response, SessionTaskError>) -> Void = {r in})
                                              -> SessionTaskType? {
...

RequestType には以下の様な記述がありました。

public protocol RequestType {
    /// The response type associated with the request type.
    associatedtype Response
...

これは Protocol の Associated Types という機能で定義するプロトコルに関連する型を指定できるものです。 以下のように RateLimitRequest の Response 型を typealias キーワードで指定することができます。

struct RateLimitRequest: RequestType {
    typealias Response = RateLimit
...

これにより先ほどの sendRequest メソッドの handler 引数にある Result<Request.Response, SessionTaskError> の記述が、RateLimitRequest の場合は Result<RateLimit, SessionTaskError> に定まるわけです。 こうして、リクエストとそれに対応するレスポンスのモデルの型を明示できるようになっています。

このようにして APIKit はリクエストとレスポンスを表現するモデルをわかりやすく定義できるように作られています。 僕のお気に入りのライブラリです。

GarageClientSwift の実装

GarageClientSwift はこれまで説明してきた Himotoki と APIKit を組み合わせて作ったライブラリです。 すでに利用例で見せたように、Himotoki を使った Decodable なリソースのモデルを用意し、APIKit のようにリクエストを表現してリクエストを送信します。

やっていることは APIKit をラップして、Garage アプリケーションの認証に必要なアクセストークンをリクエストに付与したり、Garage のレスポンスに共通で含まれるページング等の情報を持った値を表現する GarageResponse を返すようにしています。

少し工夫したところはリソースの型に User[User] のようにモデルの配列も指定できるようにしたところです。

GarageClient にふたつの sendRequest を定義しています。 https://github.com/slightair/GarageClientSwift/blob/1.1.0/Sources/GarageClient.swift

public func sendRequest<R: GarageRequestType, D: Decodable where R.Resource == D>
    (request: R,
     handler: (Result<GarageResponse<D>, SessionTaskError>) -> Void = { result in })
    -> SessionTaskType? {
        let resourceRequest = RequestBuilder.buildRequest(request, configuration: configuration)
...

public func sendRequest<R: GarageRequestType, D: Decodable where R.Resource: CollectionType, R.Resource.Generator.Element == D>
    (request: R,
     handler: (Result<GarageResponse<[D]>, SessionTaskError>) -> Void = { result in })
    -> SessionTaskType? {
        let resourceRequest = RequestBuilder.buildRequest(request, configuration: configuration)
...

リクエストの ResourceDecodable または Decodable を要素に持つ CollectionType を受け付けています。

RequestBuilder にもふたつの buildRequest を定義しており、それぞれ SingleResourceRequestMultipleResourceRequest を作ります。 https://github.com/slightair/GarageClientSwift/blob/1.1.0/Sources/RequestBuilder.swift

struct RequestBuilder {
    static func buildRequest<R: GarageRequestType, D: Decodable where R.Resource == D>
        (baseRequest: R, configuration: GarageConfigurationType) -> SingleResourceRequest<R, D> {
        return SingleResourceRequest(baseRequest: baseRequest, configuration: configuration)
    }

    static func buildRequest<R: GarageRequestType, D: Decodable where R.Resource: CollectionType, R.Resource.Generator.Element == D>
        (baseRequest: R, configuration: GarageConfigurationType) -> MultipleResourceRequest<R, D> {
        return MultipleResourceRequest(baseRequest: baseRequest, configuration: configuration)
    }
}

SingleResourceRequestMultipleResourceRequest の違いは、中で呼んでいる Himotoki のメソッドが decodeValuedecodeArray かの違いです。 ともに ResourceRequest プロトコルに準拠しており、このプロトコルは APIKit の RequestType を継承しています。 前述した GarageRequestType は APIKit の RequestType 風のプロトコルですが、実際には APIKit の sendRequest に渡す ResourceRequestRequestBuilder が作り GarageRequestType から値を取っていたのでした。 https://github.com/slightair/GarageClientSwift/blob/1.1.0/Sources/GarageRequestType.swift

今回のような範囲では Class の継承ではなく Protocol を使うとすっきりと書けます。 Swift の Protocol は Protocol extension によるデフォルト実装の提供が強力で、継承ができない struct であっても Protocol の組み合わせで拡張していくことができます。 このような Protocol を組み合わせていくプログラミング手法を Apple は Protocol Oriented Programming として提唱しています。

まとめ

GarageClientSwift というライブラリを紹介しつつ、このライブラリの実装に利用した Himotoki、 APIKit と GarageClientSwift 自身の実装を読み、Protocol や Generics を使った実装例の説明をしました。 Swift は新しい言語であり、おもしろい機能や新しいプログラミング手法を提供してくれます。単なる Objective-C の置き換えでアプリケーションを楽に記述するための言語とは捉えずに、 Swift の言語機能を使ってより柔軟で安全なコードを記述して素敵なアプリケーションを作りましょう。

クックパッド 2016 サマーインターンシップ開催します!

こんにちは! 人事部長兼エンジニアの @yoshiori です。

クックパッドでは、今年も夏に 2 つのインターンシップを開催します!!!!

Cookpad Tech Internship - Summer 2016 -

「この夏、クックパッドで腕試しをしませんか。」ということで Cookpad Tech Internship - Summer 2016 - を開催します。 このインターンシップは前後半にわかれています。 まず、前半で Rails によるアプリケーション開発フロー、iOS アプリ開発、Android アプリ開発、サービス開発論、プログラミング論、機械学習と 6 つの分野の講義を受け、課題を行ってもらいます。

そして、後半は実際に社内でメンターとなるエンジニアと一緒に開発してもらいます。そのためエンジニアとほぼ同じ権限が与えられます。全てのソースコードは見れますし、データベースにアクセスでき、個人情報などのセキュアなデータ以外はすべて見れます。もちろん社内の主要なサーバに入る環境も与えられます。 そのため、前半の講義・課題を経て、実際の業務についても問題無いだろうと認められた人が対象となります。

自分自身の技術のレベルを知る事もできますので、是非チャレンジしてみてください!!

去年の資料はコチラ(課題は去年のものを更にブラッシュアップして行う予定です)

Cookpad Tech Internship - Summer 2016 -

実践型サービス開発・5-days Internship

f:id:cookpadtech:20160610174450p:plain

そしてもう一つは、エンジニア・デザイナーで小さなチームを組み、サービス開発を行う5日間のインターンシップです。 こちらはクックパッドで 5 年間同じ形式で行なっているインターンシップで、ハッカソンのような形で集中して実際にお題にそったサービスを作ってもらいます。 ユーザーの課題についてひたすら考え抜き、悩み、それをどうやって解決するのか、そのためにはどんな物を作ればいいのかをさらに考えぬく。それを通じて実際にクックパッドでどのようにサービス開発が行われているのかを経験してもらいます。

毎日実際にサービス開発しているクックパッドのエンジニアやデザイナーからフィードバックを受けつつ、"ユーザーファーストなモノづくり" を実践してみましょう!!

実践型サービス開発・5-days Internship


僕は、今年は両方に監修として参加して、本当に会社をあげて最強の講師陣を揃えました! クックパッドの技術や開発手法に興味がある学生のみなさん、ぜひ夏休みを利用してクックパッドの夏のインターンシップに参加してみてください! みなさんからのご応募をお待ちしています!!!