Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How To Write Unmaintainable Code In Scala by Ruslans Tarasovs

Riga Dev Day
March 13, 2016
240

How To Write Unmaintainable Code In Scala by Ruslans Tarasovs

Riga Dev Day

March 13, 2016
Tweet

More Decks by Riga Dev Day

Transcript

  1. Disclaimer This presentation is Scala specific. For more general advice

    refer to the famous essay of Roedy Green: http://www.mindprod.com/jgloss/unmain.html
  2. Disclaimer The author thinks Scala is a better language than

    Java. (for most of the purposes) The author thinks that if you are a smart Java developer and did not consider moving to Scala, you are wasting your life.
  3. Disclaimer The author thinks Scala is a better language than

    Java. (for most of the purposes) The author thinks that if you are a smart Java developer and did not consider moving to Scala, you are wasting your life. The presentation might look too opinionated even to a Scala developer.
  4. Disclaimer The author thinks Scala is a better language than

    Java. (for most of the purposes) The author thinks that if you are a smart Java developer and did not consider moving to Scala, you are wasting your life. The presentation might look too opinionated even to a Scala developer. No Harm Intended
  5. What is Scala? Popular, object-oriented, functional, statically typed programming language.

    object HelloWorld extends App { p r i n t l n ( ” Riga Dev Day” ) } More classical example: object HelloWorld { def main ( args : Array [ S t r i n g ] ) : Unit = { p r i n t l n ( ” Riga Dev Day” ) } }
  6. What is Scala? Random features Type inference: // type is

    infered here var name = ” Riga Dev Day” // compilation error here name = 5 // this is the same as var name : S t r i n g = ” Riga Dev Day” String interpolation: // compiler checks the name p r i n t l n ( s ” Hello , $name” ) // no SQL injection possible s q l ” s e l e c t ∗ from u s e r s where name = $name”
  7. What is Scala? Random features Sane equals operator: val x

    = new I n t e g e r (1) val y = new I n t e g e r (1) p r i n t l n ( x = = y ) // true case class User (name : String , surname : S t r i n g ) val user1 = new User ( ”John” , ”Smith” ) val user2 = new User ( ”John” , ”Smith” ) val user3 = new User ( ”John” , ” Notsmith ” ) p r i n t l n ( user1 = = user2 ) // true p r i n t l n ( user1 = = user3 ) // false
  8. What is Scala? Random features For comprehensions: // Future[Int] below

    val la pto ps = countLaptops () val desktops = countDesktops () val t a b l e t s = countTablets () val phones = countPhones () val computers = for { l <− la pto ps d <− desktops t <− t a b l e t s p <− phones } y i e l d l ∗ 2 + d ∗ 3 − t + p ∗ l
  9. What is Scala? Random features The same thing in Java:

    CompletableFuture <In te ge r > l a p t o p s = countLaptops ( ) ; CompletableFuture <In te ge r > desktops = countDesktops ( ) ; CompletableFuture <In te ge r > t a b l e t s = countTablets ( ) ; CompletableFuture <In te ge r > phones = countPhones ( ) ; CompletableFuture <In te ge r > computers = l a p t o p s . thenCombine ( desktops , ( l , d ) −> l ∗ 2 + d ∗ 3) . thenCombine ( t a b l e t s , (sum , t ) −> sum − t ) . thenCombine ( phones , (sum , p ) −> sum + p ∗ ? ? ? ) ;
  10. What is Scala? Some killer apps Personal favorites: Akka Actors

    Play Web applications Slick LINQ like library Spark Cluster computing / big data Spray High performance HTTP and more Bonus track: Scala.js Compile Scala to JS (not GWT style)
  11. Levels Junior Unmaintainable Code Expert Writes a messy unmantainable code.

    May slow down the work of the whole team. Gets caught already during a probation period. Makes people swear his name for several months after he quits. Often betrays a profession and becomes a good developer.
  12. Levels Unmaintainable Code Expert Writes a true unmantainable code. Sabotages

    the software development on a company level. May work for years without being decyphered. Ensures job security for himself and his colleagues up to the time when the company goes bankrupt.
  13. Levels Senior Unmaintainable Code Expert Rarely has to write the

    code himself. Is so good that everyone considers him a good developer. Defines frameworks and approaches for the whole industries. Often becomes a technical lead of a huge multinational corporation.
  14. General approach Write the code like you would write in

    Java. Write the code like you would write in Java.
  15. General approach Write the code like you would write in

    Java. Write the code like you would write in Java. Write the code like you would write in Java.
  16. General approach Write the code like you would write in

    Java. Write the code like you would write in Java. Write the code like you would write in Java. Abuse Scala type system.
  17. IDE Junior would choose the worst IDE possible. Expert would

    choose the one which allows to write a bad code as quicky as possible. IntelliJ Eclipse Ensime (Emacs, Atom, Vim, Sublime)
  18. Build tool DevOps: making build slow Maven (not bad, avoid

    zinc!) ant (awesome, avoid fsc!). sbt (use old version and hieroglyphs). gradle (avoic zinc!)
  19. Build tool Maven Simple Maven pom.xml build file. https://maven.apache.org/ guides/getting-started/maven-in-five-minutes.html

    <p r o j e c t xmlns=” h t t p : //maven . apache . org /POM/ 4 . 0 . 0 ” x m l n s : x s i=” h t t p : //www. w3 . org /2001/XMLSchema−i n s t a n c e ” x s i : s c h e m a L o c a t i o n=” h t t p : //maven . apache . org /POM/ 4 . 0 . 0 h t t p : //maven . apache . org / xsd /maven −4.0.0. xsd ”> <modelVersion>4 . 0 . 0</ modelVersion> <groupId>com . mycompany . app</ groupId> <a r t i f a c t I d>my−app</ a r t i f a c t I d> <v e r s i o n>1.0−SNAPSHOT </ v e r s i o n> <packaging>j a r</ packaging> <name>Maven Quick S t a r t Archetype</name> <u r l>h t t p : //maven . apache . org</ u r l> <dependencies> <dependency> <groupId>j u n i t</ groupId> <a r t i f a c t I d>j u n i t</ a r t i f a c t I d> <v e r s i o n>4 . 8 . 2</ v e r s i o n> <scope>t e s t</ scope> </ dependency> </ dependencies> </ p r o j e c t>
  20. Build tool sbt Simple build.sbt file. http://www.scala-sbt.org/0.13/docs/Hello.html l a z

    y v a l root = ( p r o j e c t i n f i l e ( ” . ” ) ) . s e t t i n g s ( name := ” h e l l o ” , v e r s i o n := ” 1.0 ” , s c a l a V e r s i o n := ” 2 . 1 1 . 7 ” ) More complex example. o r g a n i z a t i o n := ”com . mycompany . app” name := ”my−app” v e r s i o n := ”1.0−SNAPSHOT” dependencies + = ” j u n i t ” % ” j u n i t ” % ” 4 . 8 . 2 ” % ” t e s t ”
  21. Build tool sbt Operators are just methods in Scala: class

    Account ( val money : I n t = 0) { def +(that : Account ) : Account = new Account ( t h i s . money + that . money ) } // old sbt versions like this feature a lot: :=, ˜=, <<=, + =, ++ =, <+ =, <++ = New versions still support these operators!
  22. Code Learn these by heart and do an opposite each

    time. Scala Style Guide http://docs.scala-lang.org/style/ Effective Scala http://twitter.github.io/effectivescala/ Abuse this guide as it is tailored for Java develeopers: Databricks Scala Guide https://github.com/databricks/scala-style-guide
  23. Use null! Introduction Option in Scala is similar to Optional

    in Java, but more powerful. More support in a standard library: val option1 = map get key val option2 = l i s t . headOption val option3 = Try ( convert ) . toOption More powerful syntax: val option4 = for { value1 <− option1 value2 <− option2 value3 <− option3 } y i e l d myMethod( value1 , value2 , value3 ) It so good that Scala developers do not expect null to appear anywhere.
  24. Use null! Junior Unmaintainable Code Expert Just uses null everywhere,

    just like you would do it in Java. // plain assignment val notNull : S t r i n g = n u l l p r i n t l n ( notNull ) // null // my personal favorite: options with null val d e f i n e d : Option [ S t r i n g ] = Some( n u l l ) p r i n t l n ( d e f i n e d . i s D e f i n e d ) // true d e f i n e d map ( . length ) // NullPointerException Too easy to get caught by a simple grep of null over the files.
  25. Use null! Unmaintainable Code Expert Also uses null everywhere, but

    avoids the keyword. // default value for a field val p e r f e c t S c a l a : S t r i n g = p r i n t l n ( p e r f e c t S c a l a ) // null Too easy to notice after couple of times. Trivial to find with grep.
  26. Use null! Unmaintainable Code Expert Must be more creative. //

    call from a legacy Java code def callFromJava ( notNull : S t r i n g ) : S t r i n g = notNull . trim // NullPointerException // somewhere else in Java callFromJava (map . get ( ”my. s e t t i n g ” )) Colleagues will fix to the following and fun is over. // call from a legacy Java code def callFromJava ( param : S t r i n g ) : Option [ S t r i n g ] = Option ( param ) map ( . trim ) // None // somewhere else in Java callFromJava (map . get ( ”my. s e t t i n g ” ))
  27. Use null! Unmaintainable Code Expert Becomes really clever. // wrong

    initialization order object v a l u e s { val length = name . length val name = ” Riga Dev Day” } Not clever enough. // lazy initialization object v a l u e s { lazy val length = name . length lazy val name = ” Riga Dev Day” } (used in ”thin cake pattern”)
  28. Use null! Senior Unmaintainable Code Expert Fights the one with

    his own weapon. Becomes defensive. // only get called from Scala def c a l l 1 ( param : S t r i n g ) : Option [ S t r i n g ] = Option ( param map ( . trim )) // None // or even better def c a l l 2 ( param : Option [ S t r i n g ] ) : Option [ S t r i n g ] = Option ( param ) flatMap { param = > param map ( . trim ) // None } // awesome: returns Option[Option[String]] def c a l l 3 ( param : Option [ S t r i n g ] ) = Option ( param ) map { param = > param map ( . trim ) // None } Hard to battle if there is a Java code in a system.
  29. Dependency injection! Scala developer If something could be done in

    a complex way, why to do it in some trivial way? // class with a dependency injection enabled c l a s s S e r v i c e ( r e p o s i t o r y : R e p o s i to ry ) { . . . } val s e r v i c e = new S e r v i c e ( r e p o s i t o r y ) val s e r v i c e = new S e r v i c e (mock [ Re p o s i to ry ] ) // modules! t r a i t Module { lazy val s e r v i c e : S e r v i c e = new S e r v i c e ( r e p o s i t o r y ) } object App extends Module // override the dependency object App extends Module { override lazy val s e r v i c e = new S e r v i c e 2 }
  30. Dependency injection! Junior Unmaintainable Code Expert Uses whatever he used

    in Java. Spring Guice (dangerous! too lightweight) EJB3 dependency injection Easy to get laughed at by the experienced Scala developers. No easy way to hide.
  31. Dependency injection! Unmaintainable Code Expert Uses random Scala specific frameworks,

    but prefers runtime dependency injection to work around an awesome Scala type safety. Scaldi Subcut Scala-Guice Spring-Scala
  32. Dependency injection! Senior Unmaintainable Code Expert Uses type safe approaches

    with amazing complexity. Cake Pattern (builds awesome huge cakes of dependencies) Reader monad (allows the one to use Scalaz!)
  33. Dependency injection! Cake pattern Famous and used to be one

    of the good choices. http://www.warski.org/blog/2010/12/ di-in-scala-cake-pattern/ // Interface t r a i t UserInformationComponent { // What is needed to create the component def u s e r I n f o r m a t i o n ( u s e r : User ) t r a i t U s e r I n f o r m a t i o n { def userCountry : Country } } // Implementation t r a i t UserInformationComponentImpl extends UserInformationComponent { // Dependencies t h i s : CountryRepositoryComponent = > def u s e r I n f o r m a t i o n ( u s e r : User ) = new U s e r I n f o r m a t i o n I m p l ( u s e r ) c l a s s U s e r I n f o r m a t i o n I m p l ( v a l u s e r : User ) extends U s e r I n f o r m a t i o n { def userCountry : Country { // Using the dependency c o u n t r y R e p o s i t o r y . findByEmail ( u s e r . email ) } } }
  34. Dependency injection! Cake pattern Famous and used to be one

    of the good choices. http://www.warski.org/blog/2010/12/ di-in-scala-cake-pattern/ // Usage v a l env = new UserInformationComponentImpl with CountryRepositoryComponentImpl env . u s e r I n f o r m a t i o n ( User ( ” someuser@domain . p l ” ) ) . userCountry
  35. Dependency injection! Reader monad ”Fixes” cake pattern boilerplate. http://blog.originate.com/blog/2013/10/21/ reader-monad-for-dependency-injection/

    // Define t r a i t Users { import s c a l a z . Reader def getUser ( i d : I n t ) = Reader (( u s e r R e p o s i t o r y : U s e r R e p o s i t o r y ) = > u s e r R e p o s i t o r y . get ( i d ) ) def f i n d U s e r ( username : S t r i n g ) = Reader (( u s e r R e p o s i t o r y : U s e r R e p o s i t o r y ) = > u s e r R e p o s i t o r y . f i n d ( username ) ) }
  36. Dependency injection! Reader monad ”Fixes” cake pattern boilerplate. http://blog.originate.com/blog/2013/10/21/ reader-monad-for-dependency-injection/

    // Use object U s e r I n f o extends Users { def userEmail ( i d : I n t ) = { getUser ( i d ) map ( . email ) } def u s e r I n f o ( username : S t r i n g ) = f o r { u s e r < − f i n d U s e r ( username ) boss < − getUser ( u s e r . s u p e r v i s o r I d ) } y i e l d Map( ” fullName ” − > s ”${ u s e r . firstName} ${ u s e r . lastName}” , ” email ” − > s ”${ u s e r . email}” , ” boss ” − > s ”${boss . firstName} ${boss . lastName}” ) }
  37. Use structural types! Legitimately, Scala developers use these in unit

    tests, or for reflection, for example. def f i x t u r e = new { val pi = 3 . 1 4 ; val e = 2.7 } value . asInstanceOf [{ def sum () } ] . sum () Unmaintainability experts jump on them when allowed. // awesome way to confuse people val p e r f e c t V a l u e = new { def value = 7 } // makes compilation slow def multipleReturnValues = { new { def x = 9; def y = 4 } } // uses reflection internally object MyObject {} object MyObject extends {}
  38. More random features to abuse Actors Make your POJOs impossible

    to read and claim it improves thread safety. DSLs Invent your own programming language using Scala. Implicits Use Pimp My Library Pattern to do the actual programming. Scalaz Ensure the one does not live until the compilation is finished.
  39. More random features to abuse Actors Similar to Erlang. Clever

    synchronized block in some sense. c l a s s Account { i n t money = 0; synchronized void c r e d i t ( i n t x ) { money + = x ; } synchronized void t r a n s f e r ( Account that , i n t x ) { money −= x ; that . c r e d i t ( x ) ; } } account1 . t r a n s f e r ( account2 , 5 ) ; account2 . t r a n s f e r ( account1 , 5 ) ;
  40. More random features to abuse Actors Legitimate Scala example. class

    Account extends Actor { var money = 0 def r e c e i v e = { case C r e d i t ( x ) = > money + = x case Transfer ( that , x ) = > money −= x that ! C r e d i t ( x ) } } account1 ! Transfer ( account2 , 5) account2 ! Transfer ( account1 , 5)
  41. More random features to abuse Actors Use Actors for everything!

    import U s e r R e p o s i t o r y . Command . c l a s s U s e r R e p o s i t o r y ( database : Database ) extends Actor { def r e c e i v e = { case Create ( u s e r ) = > sender ( ) ! database . save ( u s e r ) case Read ( i d ) = > sender ( ) ! database . load ( i d ) case Update ( u s e r ) = > sender ( ) ! database . save ( u s e r ) case Delete ( i d ) = > sender ( ) ! database . d e l e t e ( u s e r ) } } object U s e r R e p o s i t o r y { sealed t r a i t Command object Command { case c l a s s Create ( u s e r : User ) extends Command case c l a s s Read ( i d : S t r i n g ) extends Command case c l a s s Update ( u s e r : User ) extends Command case c l a s s Delete ( i d : s t r i n g ) extends Command } }
  42. More random features to abuse Actors Pattern to avoid. c

    l a s s UserRepository ( database : Database ) { def c r e a t e ( user : User ) : S t r i n g = database . save ( user ) def read ( i d : S t r i n g ) : User = database . load ( i d ) def update ( user : User ) : Unit = database . save ( user ) def d e l e t e ( i d : S t r i n g ) : Unit = database . d e l e t e ( i d ) }
  43. More random features to abuse DSLs So powerful, it is

    very easy to write your own DSL. v a l l i m i t = 10 v a l q1 = c o f f e e s f i l t e r ( . p r i c e < l i m i t ) map ( . name) v a l q2 = f o r ( c <− c o f f e e s ; i f c . p r i c e < l i m i t ) y i e l d c . name // SQL: select NAME from COFFEES where PRICE ¡ 10
  44. More random features to abuse DSLs So powerful, it is

    very easy to write your own DSL. https://github.com/tpolecat/basic-dsl val b = new BASIC { 10 PRINT ”HELLO WORLD” 20 INPUT ”WHAT IS YOUR NAME?” AS N$ 30 INPUT ”HOW MANY TIMES SHALL I PRINT IT?” AS N 40 IF N <> INT(N) THEN 30 50 IF N < 1 THEN 30 60 PRINT ”OK THEN: ” 70 FOR I IN 1 TO N 80 PRINT N$ 90 NEXT I 95 PRINT ”BYE. ” }
  45. More random features to abuse Implicits Used for defining type

    classes. // play-json case c l a s s User ( name : String , surname : String , c r e a t e d : DateTime ) v a l u s e r = User ( ”John” , ”Smith” , DateTime . now) Json . toJson ( u s e r ) // internals def toJson [T] ( o : T) ( i m p l i c i t t j s : Writes [T ] ) : JsValue = t j s . w r i t e s ( o ) // syntactic sugar def toJson [T: Writes ] ( o : T) : JsValue def doStuff ( x : I n t )( i m p l i c i t val timeout : I n t ) i m p l i c i t val numbers = 7 doStuff (8)
  46. Summary Writing unmaintainable code for Scala is possible, though not

    trivial. The biggest problem and your worst enemy are the talented people atracted to it. If not prepared to face them, it might be better to focus on a different programming language.