$30 off During Our Annual Pro Sale. View Details »

Lessons Learned

Lessons Learned

How to launch a product using Scala.

Feb 25, 2017 at Scala Matsuri 2014 in Tokyo, Japan.

Yoshimasa Niwa

February 25, 2017
Tweet

More Decks by Yoshimasa Niwa

Other Decks in Programming

Transcript

  1. How to launch a new product using Scala
    Lessons Learned
    Yoshimasa Niwa
    Feb 25, 2017 in Tokyo
    αʔϏεΛθϩ͔Β࡞ͬͯେ੾ͩͬͨͭͷ͜ͱ

    View Slide

  2. @niw
    Yoshimasa Niwa

    View Slide

  3. View Slide

  4. California, USA
    San Francisco
    αϯϑϥϯγεί͔Β͖·ͨ͠ɻ

    View Slide

  5. View Slide

  6. View Slide

  7. that we’ve launched.
    Products
    θϩ͔Βͭͬͨ͘ϓϩμΫτͷ঺հ

    View Slide

  8. Search GIF and instantly Tweet it.
    GIF Button
    (*'Ϙλϯػೳ

    View Slide

  9. View Slide

  10. Unleash your creativity by adding fun Stickers.
    Stickers
    εςοΧʔػೳ

    View Slide

  11. View Slide

  12. View Slide

  13. How to launch a product using Scala
    Lessons Learned
    I was (am) not a Scala specialist.
    Launched user facing products using Scala.
    There are many lessons I’ve learned.
    αʔϏεΛθϩ͔Β࡞ͬͯେ੾ͩͬͨͭͷ͜ͱ

    View Slide

  14. Today’s talk
    At glance
    History, we’re learning from history.
    Architecture, that built from the lessons.
    Implementation, that we’re using.
    Executions, form what I’ve learned lessons.
    ຊ೔ͷ࿩ͷ֓ཁ

    View Slide

  15. A short history behind that whale.
    History
    γεςϜ͕ࠓͷΑ͏ʹͳΔ·Ͱʹ͸ྺ࢙͕͋Γ·ͨ͠ɻ

    View Slide

  16. in 2009.
    The biggest Ruby on Rails application
    5XJUUFS͸ੈքͰ͓ͦΒ͘࠷΋େ͖͍3VCZPO3BJMTͷΞϓϦͰͨ͠

    View Slide

  17. View Slide

  18. View Slide

  19. So it called “Monorail.”
    Many pains in the large monolithic system
    • It’s really easy to break someone’s modules.
    • grep(1) is the only tool we can believe.
    • Deploy everything, revert everything.
    ڊେͳ୯ҰͷγεςϜͰ͸ਏ͔ͬͨɻ

    View Slide

  20. into small services in Scala
    Decompose monolithic system
    ڊେͳ୯ҰͷγεςϜΛ෼ղͯ͠4DBMBͰ࠶࣮૷͍ͯͬͨ͠

    View Slide

  21. to decompose monolithic Ruby on Rails application
    Rewrite each module in Scala
    class User < ActiveRecode::Base

    def self.find(...)

    $user_service.find(...)

    end

    end
    User.find(1) #=> #
    ڊେͳ3VCZPO3BJMTΞϓϦέʔγϣϯΛ෼ղͯͦ͠ΕͧΕͷϞδϡʔϧΛ4DBMBͰ
    ॻ͖௚ͨ͠ɻ

    View Slide

  22. into a single system.
    Compose each small service
    ෼ղͨ͠αʔϏεΛ߹੒ͯ͠ɺγεςϜͱͯ͠ػೳ͢Δ

    View Slide

  23. Deal with complexity
    “Functional” Architecture
    ʮؔ਺తͳʯΞʔΩςΫνϟͰɺෳࡶͳ࢓૊ΈʹରԠ͢Δɻ

    View Slide

  24. Goal is managing complexity.
    System is complex
    γεςϜ͸ͱͯ΋ෳࡶɻͦͷෳࡶ͞Λ͍͔ʹ؅ཧ͢Δ͔ͱ͍͏ͷ͕ΰʔϧɻ

    View Slide

  25. Example code
    System is complex
    def main(args: Array[String]) {

    args match {

    case Seq("command1") =>

    ...

    case Seq("command2") =>

    ...

    case Seq("command3") =>

    ...
    // Continue 10,000 lines

    }

    }
    ෳࡶͳίʔυͷྫ

    View Slide

  26. Divide-and-rule
    Separate interests and responsibilities
    ෳࡶͳγεςϜ͸෼ׂ౷࣏ɻॲཧͷର৅ͱ੹೚Λ෼ׂɻ

    View Slide

  27. Divide-and-rule.
    System is complex
    def func1() {

    ...

    }
    def func2() {

    ...

    }
    def func3() {

    ...

    }
    def main(args: Array[String])
    {

    args match {

    case Seq("command1") =>

    func1()

    case Seq("command2") =>

    func2()

    case Seq(“command3") =>

    func3()

    ...

    }

    }

    ෳࡶͳίʔυΛந৅Խͯ͠෼ׂͨ͠ྫ

    View Slide

  28. Getting request and returning response.
    Service is a function
    αʔόʔ͸ɺͭ·Γؔ਺ɻϦΫΤετΛҾ਺ʹͱͬͯɺϨεϙϯεΛฦ͢ɻ
    def service(

    request: Request

    ): Response

    View Slide

  29. Create new functions in the system.
    Create a new service
    ৽αʔϏεΛ࡞ΔʹγεςϜʹ৽͍ؔ͠਺Λ௥Ճ͢Δ
    def service(

    request: Request

    ): Response
    def newService(

    request: Request

    ): Response

    View Slide

  30. Response is not always available immediately.
    In real world, however.
    def service(request: Request): Response = ...
    // How slow is this?

    val reposes = service(request)

    ݱ࣮ʹ͸ɺ͔͠͠ɺؔ਺ͷ஋͸ଈ࠲ʹखʹೖΔΘ͚Ͱ͸ͳ͍ɻ

    View Slide

  31. We need to wait.
    Everything is asynchronous
    ͢΂ͯ͸ඇಉظɻ଴͕ͪ࣌ؒ͋Δɻ

    View Slide

  32. Example
    GIF Button
    (*'Ϙλϯͷ৔߹
    Loading Successful Failed

    View Slide

  33. A capsule of the value
    Option[+A] ?
    ஋Λ͍ΕΔͱ͍͑͹ɺ0QUJPO<">
    +A
    Option
    Value
    Some None

    View Slide

  34. A capsule of the value
    Future[+A]
    ͋Δ஋͕औΓ͏Δঢ়ଶΛ'VUVSFͰแΜͰ͓͘
    +A
    Future
    ? Value Exception
    Pending Successful Failed

    View Slide

  35. Example
    Future[+A]
    def func(a: String): Future[String] = …
    def main(args: Array[String]) {

    // This call is not blocking.

    val future = func(args.head)


    // Do awesome things...


    // Wait until reply is defined

    println(Await.result(future))

    }

    ྫ͑͹ɺ͜͏͍͏ײ͡ʹͳΔɻ

    View Slide

  36. Getting request and reply in Future.
    Service[-Req,+Rep]
    αʔϏε͸ϦΫΤετΛड͚औͬͯɺϨεϙϯεͷ'VUVSFΛฦ͢ɺͱॻ͚Δɻ
    Req => Future[Rep]
    Rep
    Req Waiting… Response Exception
    Pending Successful Failed

    View Slide

  37. Future works same as the other capsules.
    Compose Future[+A]
    • map[A,B](A => B): Future[B]
    • flatMap[A,B](A => Future[B]): Future[B]
    • join[A,B](Future[A], Future[B]):

    Future[(A,B)]
    • collect[A](Seq[Future[A]]): Future[Seq[A]]
    'VUVSF͸ଞͷ4DBMBͷίϨΫγϣϯͱಉ༷ʹ߹੒Մೳɻ
    A
    Future[A]
    B
    Future[B]
    map
    A
    Future[A]
    B
    Future[B]
    flatMap

    View Slide

  38. Example
    Compose service calls
    val userRepo: Service[Long, User] = ...

    val profileRepo: Service[User, Profile] = ...

    val postRepo: Service[User, Seq[Post]] = ...

    userRepo(userId).flatMap { user =>

    Future.join(

    profileRepo(user),

    postRepo(user)

    ).map { (profile, posts) =>

    renderResponse(profile, posts)

    }

    }
    αʔϏεͷ݁Ռ͸'VUVSF͔ͩΒɺݺͼग़͠Λ࣍ʑ߹੒Ͱ͖Δɻ

    View Slide

  39. class GifSearchHandler {

    def apply(request: SearchRequest):

    Future[SearchResponse] = for {

    user <- userRepo(request.user_id)

    results <- Future.collect(

    gifRepos.map { repo =>

    repo(request.query, user.lang)

    }

    )

    } yield merge(results)
    }
    GIF Button
    Example
    ྫ͑͹(*'#VUUPOͷݕࡧॲཧ͸͜Μͳײ͡

    View Slide

  40. Deal with complexity
    “Functional” Architecture
    Divide-and-rule
    Function as a service
    Compose response Future[+A] to make a logic
    asynchronously
    ʮؔ਺తͳʯΞʔΩςΫνϟ

    View Slide

  41. Things we need to build services
    Implementation
    αʔϏεΛ࡞Δͷʹඞཁͳ࣮૷

    View Slide

  42. • Define functions
    • Resolve functions
    • Implement functions
    • Instantiate functions
    • Discover functions
    Things we need
    to call function
    γεςϜ͕ඞཁͱ͍ͯ͠Δ͜ͱ

    View Slide

  43. Thrift, an IDL to define models and services
    Scrooge, a Thrift code generator in Scala
    Thrift, Scrooge
    Define functions
    ఆٛʹ͸5ISJGUΛ࢖͏ɻ4DSPPHFͰ4DBMBͷίʔυੜ੒ɻ

    View Slide

  44. Resolve functions
    Mono repo
    Git, “special version”
    Faster clone
    Pants, a build system for mono repo.
    ੩తͳґଘؔ܎͸ϞϊϨϙͱ1BOUTϏϧυγεςϜͰղܾ

    View Slide

  45. Finagle, a RPC core library.
    Finatra, a framework using Finagle
    TwitterServer (HTTP server and Thrift server)
    Guice dependency injection
    Finagle, Finatra
    Implement functions
    ࣮૷ʹ͸'JOBHMFΛϕʔεʹ'JOBUSBΛ࢖͍ͬͯΔ

    View Slide

  46. Instantiate functions
    Mesos, Aurora
    Mesos, a cluster manager for data center.
    Aurora framework, a job scheduler.
    Define job in aurora DSL file.
    .FTPTͱ"VSPSBϑϨʔϜϫʔΫͰΠϯελϯεΛ؅ཧ

    View Slide

  47. Discover functions
    Zookeeper
    Zookeeper, a reliable directory for service discovery.
    Announced by Aurora
    Resolved by Finagle
    ;PPLFFQFSͰαʔϏεΛൃݟ͢Δɻ"VSPSBͰΠϯελϯεΛ໊લొ࿥ͯ͠ɺ'JOBHMF
    Ͱղܾ

    View Slide

  48. .scala
    .thrift
    .aurora
    .scala
    .thrift
    .aurora
    Git
    .scala
    .thrift
    .aurora
    Mesos
    Aurora
    Zookeeper
    /s/gifsearch
    Instance
    Instance

    View Slide

  49. Things we need to build services
    Implementations
    Define in Thrift, manage every things in a single Git
    repository.
    Finatra to implement services.
    Aurora to run services and discovery.
    αʔϏεΛ࡞Δͷʹඞཁͳ࣮૷

    View Slide

  50. Executions
    Let’s talk about making things.
    ੡࡞ਐߦʹ͍ͭͯ

    View Slide

  51. We’re a team
    Engineers, product manager, and designers works in a team.
    ΤϯδχΞɺϓϩμΫτϚωʔδϟɺσβΠφ͕ɺͻͱͭͷνʔϜͰ࢓ࣄ͠·͢ɻ

    View Slide

  52. P
    Product
    Manager
    D
    Designer
    E
    Engineer
    Design
    Plan
    Implement

    View Slide

  53. Engineers have different responsibilities than Product Managers
    Product vs. Engineering
    ϓϩμΫτରΤϯδχΞϦϯά
    ΤϯδχΞ͸ϓϩμΫτϚωʔδϟͱ͸ҧ͏੹೚͕͋Δ

    View Slide

  54. P
    Product
    Manager
    D
    Designer
    E
    Engineer
    Design
    Plan
    Implement

    View Slide

  55. Maximize meeting product requirements
    Minimize tech-debt
    ϓϩμΫτͷཁٻʹ౴͑ͭͭɺٕज़తͳෛͷҨ࢈ΛݮΒ͢ɻ

    View Slide

  56. Integrate everything at first.
    Build app from top to bottom
    Bad assumption may increase tech-debt.
    Right assumption may reduce tech-debt.
    Reduces many bad assumptions.
    ·্͔ͣΒԼ·Ͱͱʹ͔͘ܨ͍Ͱಈ͔͢ɻ૝ఆΛͰߟ͑Δ͜ͱΛͰ͖Δ͚ͩݮΒ͢ɻ
    S S
    S S
    S
    Service Service
    Service
    Service Service

    View Slide

  57. Create many instances with modification on Aurora
    Run devel instances
    Work-in-progress instance is okay.
    Working mock is better than tech-doc to deal with
    product manager, sometimes.
    ։ൃ༻ΠϯελϯεΛ࣮ߦ͓ͯ͘͠ɻ

    View Slide

  58. Override services for debugging and testing
    Dtab
    A delegation table.
    Override finagle resolver to swap services.
    Dtab-local: header for HTTP request.
    %UBCΛ࢖໊ͬͯલղܾΛ্ॻ͖ͯ͠αʔϏεΛϥϯλΠϜʹೖΕସ͑Δ

    View Slide

  59. Naming and Dtab
    Naming
    /s/gifsearch
    /s/prod/gifsearch
    /zk/zk.domain:2181/prod/gifsearch
    /$/com.twitter.serverset/zk.domain:2181/prod/
    gifsearch
    αʔϏεͷ໊લղܾͱ%UBC

    View Slide

  60. Naming and Dtab
    Naming
    /s/gifsearch
    /s/prod/gifsearch
    /s/devel/gifsearch
    /zk/zk.domain:2181/devel/gifsearch
    /$/com.twitter.serverset/zk.domain:2181/devel/
    gifsearch
    αʔϏεͷ໊લղܾͱ%UBC
    /s/prod/gifsearch

    =>/s/devel/gifsearch;

    View Slide

  61. Deal with anomalies and exceptional cases
    Design vs. Engineering
    σβΠϯରΤϯδχΞϦϯά
    ྫ֎ʹ͍͔ʹରԠ͢Δ͔

    View Slide

  62. P
    Product
    Manager
    D
    Designer
    E
    Engineer
    Design
    Plan
    Implement

    View Slide

  63. by maintain the application state.
    Reduce exceptional cases
    ΞϓϦέʔγϣϯͷঢ়ଶ؅ཧΛͯ͠ྫ֎ΛݮΒ͢ɻ

    View Slide

  64. are user facing and used by human beings.
    iOS and Android clients
    ϞόΠϧΫϥΠΞϯτ͸ਓ͕͔ؒͭ͏΋ͷɻ༧ଌ֎ͷૢ࡞Λ͞ΕΔ͜ͱ΋͋Δɻ

    View Slide

  65. User interface is a representation of internal states.
    Manage internal state.
    Users changes the internal state.
    React to the internal state changes.
    Applying animation, if needed.
    ಺෦ঢ়ଶͱมߋͷྲྀΕΛ؅ཧ͢Δɻ6*͸಺෦ঢ়ଶΛදݱ͢Δ΋ͷɻ
    S
    Action State Change
    UI
    Update
    Action
    U
    User

    View Slide

  66. Communication between teams and services
    Engineering vs. Engineering
    ΤϯδχΞϦϯάରΤϯδχΞϦϯά
    νʔϜಉ࢜ͷ΍ΓͱΓ

    View Slide

  67. P
    Product
    Manager
    D
    Designer
    E
    Engineer
    Design
    Plan
    Implement
    E
    Engineer
    nt
    Im
    plem
    ent

    View Slide

  68. Truth is in front of you as a code.
    Read code
    ৴͡ΒΕΔ΋ͷ͸ιʔε͚ͩɻ

    View Slide

  69. Single “source” of truth.
    Mono repo helps it.
    Identify exact source code by deploy SHA-1.
    Browse entire source code that the system is using.
    ୯ҰιʔεϨϙδτϦͰਅ࣮ΛݟΔ
    commit 8270324cdf0d1e60a83dbefa1de29d89f6107587

    commit f40648b9105f7478139694acd0821c15d3315dc2

    commit fa0dc865887a02b49576bdc1de57f4ad802d4710

    commit ebf31743cbe3008ec311d88aea42b987b0288cd3

    View Slide

  70. Name tells what it is.
    Define right functions
    Define API in reasonable way.
    Naming is really really important.
    They never read tech-doc.
    ਖ਼͍ؔ͠਺ͷఆٛ͸υΩϡϝϯτΑΓେ੾ɻ
    outputImageProviderFromBufferWithPixelFormat:pixelsW
    ide:pixelsHigh:baseAddress:bytesPerRow:releaseCallba
    ck:releaseContext:colorSpace:shouldColorMatch:

    View Slide

  71. Manage dependencies.
    Downstream
    Estimate QPS and latency.
    Right timeout and retry policy.
    Right load banaler
    ApertureLoadBalancer for lower QPS
    214ͱϨΠςϯγΛ؅ཧɻϥΠϜΞ΢τͱϦτϥΠͷௐ੔ɺϩʔυόϥϯαͷௐ੔ɻ
    S S
    S S
    S
    Service Service
    Service
    Service Service

    View Slide

  72. Manage dependencies
    Upstream
    Look at CancelledRequestException.
    Upstream timed out for downstream, it will cancel
    request and downstream will see this exception.
    $BODFMMFE3FRVFTU&YDFQUJPOʹ஫ҙɻ
    6QTUSFBN͕ݫ͍͠λΠϜΞ΢τΛ࣋ͬͯΔՄೳੑ͋Γɻ
    S S
    Service Service
    Timeout!
    Cancel

    Exception
    Timeout

    Exception

    View Slide

  73. Conclusion
    Lessons Learned
    ·ͱΊ

    View Slide

  74. How to launch a product using Scala
    Lessons Learned
    1. Build app from top to bottom

    to deal with product managers.
    2. Manage internal state

    to deal with designers.
    3. Read code

    to deal with engineers.
    αʔϏεΛθϩ͔Β࡞ͬͯେ੾ͩͬͨͭͷ͜ͱ

    View Slide

  75. #thankyou
    @niw
    Questions?
    ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ

    View Slide