Vaidas Pilkauskas - Comprehensible code

Preview:

Citation preview

This space intentionally left blank

COMPREHENSIBLE CODECOMPREHENSIBLE CODE2017, Vaidas Pilkauskas

@liucijus2017, Vaidas Pilkauskas

@liucijus

GREEN

YELLOW

BLUE

PINK

PINK YELLOWGREENBLUE

REDBLACK GREY

BROWN

COMPREHENSIBLE CODECOMPREHENSIBLE CODE2017, Vaidas Pilkauskas2017, Vaidas Pilkauskas

@liucijus

/** * User: Vaidas_pilkauskas * Date: 17/11/10 * Time: 15:12 */

…, programs must be written for people to read, and only incidentally for

machines to execute

—SICP

WHO READSTHE CODE?

WHO READSTHE CODE?

mrhayata | Flickr

YOU

THE FUTURE YOU

USERS

CONTRIBUTORS

FUTURE MAINTAINERS

READABLE CODEOptimized for reading

Cleanly editedExplicitly structured

https://wallpaperscraft.com/download/cat_window_broken_peep_104131/1920x1080

Lennart van Uffelen

COMPREHENSION

LONG LINES

HOW LONG?

Readable is less than 80, but:

→ Intellij IDEA has default marker at 120

Many tools work with 120

EXAMPLES OF VISUAL NOISE

INCONSISTENCY

UNCONVENTIONAL STYLE

TOOL WARNINGS

EXCESSIVE EMPTY LINES

UNUSED IMPORTS

UNFORMATTED CODE

IGNORING WARNINGS

IGNORING WARNINGS

Puts Broken Window into effect

IGNORING WARNINGS

False warnings hide important ones

IGNORING WARNINGS

Distractions (context switch)Inbox Zero problem

DEALING WITH WARNINGSFix the code—the best option

Disable/configure warnings to fit the code

Ask vendor to provide good defaults

ILLOGICAL CODE STRUCTURE

LOGICAL STRUCTURE

Group of elements which are in the same level of abstraction

aspects.setAspect(Web(request.url, request.remoteIp, request.remotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent), request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName))

aspects.setAspect(Web(request.url, request.remoteIp, request.remotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent), request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName))

aspects.setAspect(Web(request.url, request.remoteIp, request.remotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent), request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName))

aspects.setAspect(Web(request.url, request.remoteIp, request.getRemotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent, request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName))

aspects.setAspect(Web(request.url, request.remoteIp, request.getRemotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent, request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName))

aspects.setAspect(Web(request.url, request.remoteIp, request.remotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent), request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName))

aspects.setAspect( Web( request.url, request.remoteIp, request.remotePort, request.localIp, request.localUrl, UserAgent.from(request.userAgent), request.cookieDomain, request.appUrl, Id(request.requestId), request.dateTime, request.isRpcRequest, WebGeo(request.geo), request.getServerName ))

aspects.setAspect( Web( url = request.url, remoteIp = request.remoteIp, remotePort = request.remotePort, localIp = request.localIp, localUrl = request.localUrl, userAgent = UserAgent.from(request.userAgent), cookieDomain = request.cookieDomain, appUrl = request.appUrl, id = Id(request.requestId), timestamp = request.dateTime, isRpcRequest = request.isRpcRequest, geo = WebGeo(request.geo), serverName = request.getServerName ))

val webAspect = Web( url = request.url, remoteIp = request.remoteIp, remotePort = request.remotePort, localIp = request.localIp, localUrl = request.localUrl, userAgent = UserAgent.from(request.userAgent), cookieDomain = request.cookieDomain, appUrl = request.appUrl, id = Id(request.requestId), timestamp = request.dateTime, isRpcRequest = request.isRpcRequest, geo = WebGeo(request.geo), serverName = request.getServerName)

aspects.setAspect(webAspect)

val client = Client( name = ..., addresses = Seq( Address( type = Work, building = Building( room = Room(301), street = Street(“Rūdninkų”), city = City(“Vilnius”), zip = Zip(“123123”), ) ) ))

ARGH! BRACES!!1

{ “foo”: “bar”}

<foo> <bar /></foo>

Foo( “bar”)

{ “foo”: “bar”}

<foo> <bar /></foo>

Foo( “bar”)

Tools and Formatting

val message = ContactActivityMessage(builder.tenantDetails.metaSiteId.getId.asGuid, constructNotifyActivityInfoDto(userActivity), constructNotifyContactInfoDto(contact))

val message = ContactActivity(builder.tenantDetails.metaSiteId.getId.asGuid, constructNotifyActivityInfoDto(userActivity), constructNotifyContactInfoDto(contact))

val message = ContactActivity( builder.tenantDetails.metaSiteId.getId.asGuid, constructNotifyActivityInfoDto(userActivity), constructNotifyContactInfoDto(contact))

val message = ContactActivityMessage( builder.tenantDetails.metaSiteId.getId.asGuid, constructNotifyActivityInfoDto(userActivity), constructNotifyContactInfoDto(contact))

Examples

val asSiteContact = contact.details.asSiteContact(count) .copy(lastActivity = updated)

// BAD: Chain logic group split

val asSiteContact = contact.details.asSiteContact(count).copy(lastActivity = updated)

// GOOD: Chaining on the same line

val asSiteContact = contact .details .asSiteContact(count) .copy(lastActivity = updated)

// GOOD: Chain member per line

enum State { READ, WRITE, PAUSE, STOP}

// BAD: Broken logical group

enum State { READ, WRITE, PAUSE, STOP}

// WORSE: Looks clean, but has broken logical group

enum State { READ, WRITE, PAUSE, STOP}

enum State { READ, WRITE, PAUSE, STOP}

// GOOD

val foo = Foo(Bar(baz = Varoom( prop1 = "val", prop2 = "val2", prop3 = "val3), faz = Seq("one", "two")))

operations.query(contactById, Map( "site_id" -> getBytes(siteId), "contact_id" -> getBytes(contactId)), ContactMapper)

// BAD: hard to spot relations

val foo = Foo( Bar( baz = Varoom(prop1 = "val", prop2 = "val2", prop3 = "val3), faz = Seq("one", "two") ))

operations.query( contactById, Map( "site_id" -> getBytes(siteId), "contact_id" -> getBytes(contactId) ), ContactMapper)

// GOOD: explicit relations

case class Activity(contact: Contact, created: Instant, payload: String)

// BAD: fields in the same logical group given uneven placement

case class Activity(contact: Contact, created: Instant, payload: String)

case class Activity( contact: Contact, created: Instant, payload: String)

case class Activity( contact: Contact, created: Instant, payload: String)

// GOOD: all fields are equally significant

CAN STRUCTURE BE AUTOMATED?

Concentrate on communicating intent,but Automate repetitive work

There are other ways to communicate

CODE COMPREHENSIONis based on

NAMED ABSTRACTIONS and CODE STRUCTURE

THANK YOU!

QUESTIONS?

Recommended