Lessons Learned

Lessons Learned

How to launch a product using Scala.

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

E4e01fb8b7105e61e3876224139503ab?s=128

Yoshimasa Niwa

February 25, 2017
Tweet

Transcript

  1. How to launch a new product using Scala Lessons Learned

    Yoshimasa Niwa Feb 25, 2017 in Tokyo αʔϏεΛθϩ͔Β࡞ͬͯେ੾ͩͬͨͭͷ͜ͱ
  2. @niw Yoshimasa Niwa

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

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

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

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

  11. None
  12. None
  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. αʔϏεΛθϩ͔Β࡞ͬͯେ੾ͩͬͨͭͷ͜ͱ
  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. ຊ೔ͷ࿩ͷ֓ཁ
  15. A short history behind that whale. History γεςϜ͕ࠓͷΑ͏ʹͳΔ·Ͱʹ͸ྺ࢙͕͋Γ·ͨ͠ɻ

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

  17. None
  18. None
  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. ڊେͳ୯ҰͷγεςϜͰ͸ਏ͔ͬͨɻ
  20. into small services in Scala Decompose monolithic system ڊେͳ୯ҰͷγεςϜΛ෼ղͯ͠4DBMBͰ࠶࣮૷͍ͯͬͨ͠

  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) #=> #<UserServiceUser: ...> ڊେͳ3VCZPO3BJMTΞϓϦέʔγϣϯΛ෼ղͯͦ͠ΕͧΕͷϞδϡʔϧΛ4DBMBͰ ॻ͖௚ͨ͠ɻ
  22. into a single system. Compose each small service ෼ղͨ͠αʔϏεΛ߹੒ͯ͠ɺγεςϜͱͯ͠ػೳ͢Δ

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

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

  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
 }
 } ෳࡶͳίʔυͷྫ
  26. Divide-and-rule Separate interests and responsibilities ෳࡶͳγεςϜ͸෼ׂ౷࣏ɻॲཧͷର৅ͱ੹೚Λ෼ׂɻ

  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()
 ...
 }
 }
 ෳࡶͳίʔυΛந৅Խͯ͠෼ׂͨ͠ྫ
  28. Getting request and returning response. Service is a function αʔόʔ͸ɺͭ·Γؔ਺ɻϦΫΤετΛҾ਺ʹͱͬͯɺϨεϙϯεΛฦ͢ɻ

    def service(
 request: Request
 ): Response
  29. Create new functions in the system. Create a new service

    ৽αʔϏεΛ࡞ΔʹγεςϜʹ৽͍ؔ͠਺Λ௥Ճ͢Δ def service(
 request: Request
 ): Response def newService(
 request: Request
 ): Response
  30. Response is not always available immediately. In real world, however.

    def service(request: Request): Response = ... // How slow is this?
 val reposes = service(request)
 ݱ࣮ʹ͸ɺ͔͠͠ɺؔ਺ͷ஋͸ଈ࠲ʹखʹೖΔΘ͚Ͱ͸ͳ͍ɻ
  31. We need to wait. Everything is asynchronous ͢΂ͯ͸ඇಉظɻ଴͕ͪ࣌ؒ͋Δɻ

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

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

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

    Value Exception Pending Successful Failed
  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))
 }
 ྫ͑͹ɺ͜͏͍͏ײ͡ʹͳΔɻ
  36. Getting request and reply in Future. Service[-Req,+Rep] αʔϏε͸ϦΫΤετΛड͚औͬͯɺϨεϙϯεͷ'VUVSFΛฦ͢ɺͱॻ͚Δɻ Req =>

    Future[Rep] Rep Req Waiting… Response Exception Pending Successful Failed
  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
  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͔ͩΒɺݺͼग़͠Λ࣍ʑ߹੒Ͱ͖Δɻ
  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ͷݕࡧॲཧ͸͜Μͳײ͡
  40. Deal with complexity “Functional” Architecture Divide-and-rule Function as a service

    Compose response Future[+A] to make a logic asynchronously ʮؔ਺తͳʯΞʔΩςΫνϟ
  41. Things we need to build services Implementation αʔϏεΛ࡞Δͷʹඞཁͳ࣮૷

  42. • Define functions • Resolve functions • Implement functions •

    Instantiate functions • Discover functions Things we need to call function γεςϜ͕ඞཁͱ͍ͯ͠Δ͜ͱ
  43. Thrift, an IDL to define models and services Scrooge, a

    Thrift code generator in Scala Thrift, Scrooge Define functions ఆٛʹ͸5ISJGUΛ࢖͏ɻ4DSPPHFͰ4DBMBͷίʔυੜ੒ɻ
  44. Resolve functions Mono repo Git, “special version” Faster clone Pants,

    a build system for mono repo. ੩తͳґଘؔ܎͸ϞϊϨϙͱ1BOUTϏϧυγεςϜͰղܾ
  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Λ࢖͍ͬͯΔ
  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ϑϨʔϜϫʔΫͰΠϯελϯεΛ؅ཧ
  47. Discover functions Zookeeper Zookeeper, a reliable directory for service discovery.

    Announced by Aurora Resolved by Finagle ;PPLFFQFSͰαʔϏεΛൃݟ͢Δɻ"VSPSBͰΠϯελϯεΛ໊લొ࿥ͯ͠ɺ'JOBHMF Ͱղܾ
  48. .scala .thrift .aurora .scala .thrift .aurora Git .scala .thrift .aurora

    Mesos Aurora Zookeeper /s/gifsearch Instance Instance
  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. αʔϏεΛ࡞Δͷʹඞཁͳ࣮૷
  50. Executions Let’s talk about making things. ੡࡞ਐߦʹ͍ͭͯ

  51. We’re a team Engineers, product manager, and designers works in

    a team. ΤϯδχΞɺϓϩμΫτϚωʔδϟɺσβΠφ͕ɺͻͱͭͷνʔϜͰ࢓ࣄ͠·͢ɻ
  52. P Product Manager D Designer E Engineer Design Plan Implement

  53. Engineers have different responsibilities than Product Managers Product vs. Engineering

    ϓϩμΫτରΤϯδχΞϦϯά ΤϯδχΞ͸ϓϩμΫτϚωʔδϟͱ͸ҧ͏੹೚͕͋Δ
  54. P Product Manager D Designer E Engineer Design Plan Implement

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

  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
  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. ։ൃ༻ΠϯελϯεΛ࣮ߦ͓ͯ͘͠ɻ
  58. Override services for debugging and testing Dtab A delegation table.

    Override finagle resolver to swap services. Dtab-local: header for HTTP request. %UBCΛ࢖໊ͬͯલղܾΛ্ॻ͖ͯ͠αʔϏεΛϥϯλΠϜʹೖΕସ͑Δ
  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

  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;
  61. Deal with anomalies and exceptional cases Design vs. Engineering σβΠϯରΤϯδχΞϦϯά

    ྫ֎ʹ͍͔ʹରԠ͢Δ͔
  62. P Product Manager D Designer E Engineer Design Plan Implement

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

  64. are user facing and used by human beings. iOS and

    Android clients ϞόΠϧΫϥΠΞϯτ͸ਓ͕͔ؒͭ͏΋ͷɻ༧ଌ֎ͷૢ࡞Λ͞ΕΔ͜ͱ΋͋Δɻ
  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
  66. Communication between teams and services Engineering vs. Engineering ΤϯδχΞϦϯάରΤϯδχΞϦϯά νʔϜಉ࢜ͷ΍ΓͱΓ

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

    E Engineer nt Im plem ent
  68. Truth is in front of you as a code. Read

    code ৴͡ΒΕΔ΋ͷ͸ιʔε͚ͩɻ
  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
  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:
  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
  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
  73. Conclusion Lessons Learned ·ͱΊ

  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. αʔϏεΛθϩ͔Β࡞ͬͯେ੾ͩͬͨͭͷ͜ͱ
  75. #thankyou @niw Questions? ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ