rcov 1.8 simple cov 1.9.x
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
Metrics
- LOC, Cyclomatic Complexity
- Halstead Complexity Measures - how difficult to understand
Version Control History
- 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
- Try it out on a low-stakes feature / in house app
- Instrument and log everything
– http://apidock.com/rails/ActiveSupport/Notifications – http://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
- 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
- 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
- No code is faster than no code.
- Consider not using an ORM for high perf
- Don’t be afraid to write code
- 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
- 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”)
- 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 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
- 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
- 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
- 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
- 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)
- 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
- 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
- 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
- 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)
- def crazy_insane_method return clean_new_method if new_condition #old crazy ugliness end
- 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
- 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
- queuety, tolk
– plugin pain — Keeping plugins up to date — cached_externals or Bundler — supporting multiple rails versions
- queenbee for logins; redirect from login link to queenbee, then send them back
- availability, what if queenbee is down?
–
- REST API
- can handle errors when the remote service is unavailable
- can be a supplement to redirect-to-service
- Network latency
- unreliability
- 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