You know what you want to do, but your code just isn’t cooperating. Maybe it has a few too many levels of indentation, or chains a half dozen methods, or looks asymmetrical. Whatever it is, something just feels off. You could ignore it – I mean, you have a backlog full of features you still want to write, and it’s really not that bad. But that would be a mistake: Your code is trying to tell you something, and you don’t want to miss it.

If you can learn to tell when your code feels strange, you will quickly and drastically improve your software design skills. This intuition is hard to build, since it comes from experience, mentorship, and code reviews. But you can get some help from libraries that use syntactic vinegar to make bad code feel bad.

What does syntactic vinegar look like?

Here’s an example of syntactic vinegar using minitest/mock, a small mocking and stubbing library that ships with Ruby:

require 'minitest/mock' class CartTest < MiniTest :: Test def test_error_message_set_on_charge_failure cart = Cart . new ( items ) cart . stub ( :charge! , false ) do cart . checkout! assert_equal "The credit card could not be charged" , cart . credit_card_error end end end

When you run the test, the charge! method on Cart is stubbed, so the test won’t hit the payment processor. The block syntax is nice for making sure you only stub exactly when you want to. But what happens when you want to stub a bunch of methods?

require 'minitest/mock' class CartTest < MiniTest :: Test def test_error_message_set_on_charge_failure payment_processor = PaymentProcessor . new cart = Cart . new ( items , processor: payment_processor ) payment_processor . stub ( :charge! , false ) do payment_processor . stub ( :login! , true ) do payment_processor . stub ( :logout! , true ) do cart . checkout! assert_equal "The credit card could not be charged" , cart . credit_card_error end end end end end

Ew. That’s a lot of indentation. And that’s just in a single test – you can imagine this code being repeated in a lot of other tests.

You could wrap all this nesting into a test helper method. But if you’re really listening to your code, it’s telling you that you should find a better way. It might be time to look into using a Test Double instead:

class TestPaymentProcessor < PaymentProcessor def login! ( account_id , key ) true end def charge! ( amount , credit_card ) credit_card . can_be_charged? end def logout! true end end class CartTest < MiniTest :: Test def test_error_message_set_on_charge_failure test_payment_processor = TestPaymentProcessor . new cart = Cart . new ( items , processor: test_payment_processor ) cart . credit_card = failing_credit_card cart . checkout! assert_equal "The credit card could not be charged" , cart . credit_card_error end end

Now your test is more readable. Plus, you have a TestPaymentProcessor that can be used in a lot of other places. You could even use it in development mode, if you don’t want to hit a real server!

Bad code should feel bad

By using opinionated libraries that make bad code obvious, you’ll start to notice bad code much faster and more reliably. And that’ll make your future code cleaner, easier to read, and less painful to work with.

What are your favorite opinionated libraries, and how do they help you find and fix bad code? Let me know in the comments below!