Skip to content

Instantly share code, notes, and snippets.

@jwinter
Created May 18, 2011 19:38
Show Gist options
  • Save jwinter/979363 to your computer and use it in GitHub Desktop.
Save jwinter/979363 to your computer and use it in GitHub Desktop.

rcov 1.8 simple cov 1.9.x

SOLID Principles behind Rails 3.1

Single responsibility principle

  • One reason for a class to change
  • Makes Top-down comprehensibility (admittedly)
  • Better for maintainability,

Open/closed principle

  • Ruby classes are open for extension (good), but not closed for modification (bad)
  • monkey patching

Liskov substitution principle

  • derived classes must be substitutable for the base classes

Interface Segregation Principle

  • Clients should depend on as narrow a protocol as possible
  • ActiveModel has Six methods in its protocol
  • Code to well-defined and narrow protocols

Dependency Inversion

  • depend on abstractions, not concretions
  • duck typing, Rack just depends on just having a “call” method
  • can use a middleware on a single controller

Mining Rails - Learning from your App’s Lifeline

Metrics

  • LOC, Cyclomatic Complexity
  • Halstead Complexity Measures - how difficult to understand

Version Control History

Polyglot Persistence

Scale

  • prototype, ship
  • ad hoc queries, add indices
  • database indices -> specialized storage

– your db indices don’t fit in memory OR – your data structures don’t fit in a relational db – after_commit :update_cache – – CACHE.get(“user:#{email}”)

  • Slow processes -> background processing
  • Relax consistency requirements
  • know your datastores shortcomings
  • choose client library
  • integrate app code
  • integrate deployment (init scripts, monitoring, log rotation, etc.)
  • integrate data migration version keys, read-repair

Getting over The Fear

  • Try it out on a low-stakes feature / in house app
  • Instrument and log everything

http://apidock.com/rails/ActiveSupport/Notificationshttp://apidock.com/rails/ActiveSupport/BacktraceCleaner

  • Feature switches, progressive rollout

– rollout gem https://github.com/jamesgolick/rollout

  • Double writes and dark reads

– write to both your old system and your new system – read out of your new system, but discard the result (verify as well) – these are good for realistic production performance testing

  • Every day, iterate

Never use an ORM

Think about your interface!

  • lazy interfaces
  • site.memberships.create(:user => user.id, :owner => true)
  • Use a presenter for creating Json / paginating
  • Content.paginate(a million options)

– site.content_for_date(date, params[‘page’])

  • Reveal the intent in the code

Think about your Data

  • store the data how you use it
  • rsmaz, compress short strings you’re not indexing/searching on
  • msgpack -
  • partial updates - send the pieces that changed over the wire
  • unused fields

Think about your code

  • No code is faster than no code.
  • Consider not using an ORM for high perf
  • Don’t be afraid to write code

21 deployment tips in 50 minutes

  • vagrant rocks
  • capistrano organization

– config/deploy/recipes/

  • deploy with rvm
  • capistrano gateway

– don’t exose sshd on all server you want to deploy to – deploy through a single cap gateway

  • speeding up with git
  • capistrano multi-stage, cap-ext gem
  • bundler/capistrano.rb
  • exception_notification gem
  • generic deploy user
  • capistrano notification, cap-gun email
  • development database dumps

– run off replication read slave to avoid i/o contention on the master – database sanitization, set all passwords to password — clean out customer specific data, address, email addresses, etc.

  • log rotation

– apache pipe log rotation – CustomLog – “|/usr/bin/rotatelogs /var/log/access_log 86400” – common

  • Monitoring passenger

– passenger memory stats – Admintools Memorystats

  • ssh-agent

Testing JavaScript

Overview

  • unit testing for widgets, etc
  • integration tests for complex dom interation, ajax,
  • Capybara.current_driver = ‘Selenium’ # can switch on a test-by-test basis
  • rack-test, no js support, but fast
  • page.should have_content(“some text from the page”)
  • page.should have_no_content(“this shouldn’t be displayed”)

Capybara advice

  • test from the user’s perspective

– remove noise that the user’s wouldn’t understand

  • use modules to create your own dsl
  • avoid complicated css selectors (particularly in cucumber)
  • add_selectors via the capybara api
  • assert on visible things (not urls, cookies, Session, etc)

Use evergreen + jasmine for testing

  • use jasmine for js testing
  • evergreen, packages up jasmine

– No generators, bundled within gem

  • rake spec:javascripts

– Or reload the page? not sure which page

  • will use any capybara driver
  • rake spec:javascripts CHROME=true

More advice

  • write testable code

– structure your js code into functions or objects

  • No jQuery DOM spaghetti
  • Think about using prototypes
  • try to maintain a consistent feel to js code
  • separate into files, not a giant ball of application.js
  • mostly test event bindings (you don’t have to test all of ‘em)

– let your integration testing test them

  • Write custom matchers!
  • Isolate Ajax to avoid brittle tests
  • treat templates as fixtures

– update them manually

  • use integration test to validate templates

– don’t use the views as template fixtures (not sure about that one) – says b/c views change so frequently

  • (as an aside - don’t write view specs)
  • they don’t have CI integration

Debugging ruby performance - Aman Gupta

C, Linux, networks, cpu, and memory usage

  • lsof -nPp <pid>
  • tcpdump
  • write the data to a file, load it up in wireshark
  • strace, trace systems calls and signals (jump into kernelspace)
  • SIGVTALRM, ruby 1.8 every 10msec to switch threads (green threads)
  • posix-spawn instead of fork-exec, check out posix-spawn gem
  • ltrace

– ltrace -c ruby foo.rb # summary mode

  • memcpy’ing a ton of data on the thread stack
  • rbtrace
  • rbtrace -p 20052 –slow=50 -m ‘include?(__source__)’
  • Google perftools

– – sampling profiler

  • perftool.rb (ruby wrapper around google perftools)

– Rack::PerftoolsProfiler (set number of workers down to 1 if using unicorn or passenger)

  • gdb.rb !

– where, – ruby eval – ruby objects strings – ruby threads

  • memprof
  • Memprof.track - takes a block (like bleak_house)
  • hotspots (new gem)

https://github.com/tmm1/hotspots

Testing Legacy Apps

  • Legacy Code Is “Code based on lost requirements”
  • You don’t know what “correct” behavior is
  • Respect working code. Do no harm.
  • Code needs to evolve
  • At a sustainable pace
  • Improve Code Quality
  • Boy Scout Rule

Bad ideas

  • Covering the whole app w/tests is not a quick win
  • Dropping a quick fix in w/o tests is a bad idea

– Sets a bad precedent (that tests are not valuable)

Good ideas

  • Follow best practices for new changes only (add tests, etc)
  • over time, coverage and quality grow organically

– the parts that change frequently will get covered (and they’re more important) – not a lot of value in adding tests to verify that working code that’s not changing

It’s not easy

  • You don’t know what to test
  • It’s hard to isolate objects under test
  • Do your changes in a git branch
  • git bisect is your friend

Cucumber

  • can cover a lot of the app quickly
  • it’s so slowwwww
  • not great at isolating where a problem is
  • decent for regressions
  • run a failing test to see what the code will do, then capture that in a test
  • In a TDD process, tests are the source of truth NOT code
  • Legacy apps, code is the source of truth

Mock testing

  • almost the opposite advantages of cucumber tests
  • very fast and allow unit testing
  • they can be hard to set up (whackamole on mocking each dependency)
  • lots of mocks make the tests brittle (what if you just want to change the name)

Isolation

  • def crazy_insane_method return clean_new_method if new_condition #old crazy ugliness end

Seam

  • subclass for a test
  • x = Purchase.new; x.def bang ; end ; x.bang
  • use new defaulted to nil arguments (keep existing calls working)
  • Use a pebble

– defines method_missing, returns a tracer that says who caller – you can access your call stack from any point in ruby

What about sucky tests?

  • Make sure the suite runs green
  • Tests are also code
  • 5-minute rule

– If you look at a test and you can’t figure out what it’s doing in 5 minutes, Delete It! – not why it’s not passing

  • Complex refactors should be tied to actual new requests
  • Test at as a high a level as you can
  • Isolate code under test
  • Consider

Rails services - Jamis Buck

plugins

  • queuety, tolk

– plugin pain — Keeping plugins up to date — cached_externals or Bundler — supporting multiple rails versions

Redirect-to-service

  • queenbee for logins; redirect from login link to queenbee, then send them back
  • availability, what if queenbee is down?

Web-Service

  • REST API
  • can handle errors when the remote service is unavailable
  • can be a supplement to redirect-to-service
  • Network latency
  • unreliability

Shared database

  • eliminates much of the network-related headache
  • separate from the main app database
  • comes with its own models
  • engine-style plugin for shared models
  • override self.establish_connection

– Downsides below –

  • Tight integration makes it har dto work around the shared database being unavailable
  • SPOF
  • Migrations
  • High contention
  • Shared fixtures
  • PCI concerns
  • Testing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment