AV-Portal 3.23.3 (4dfb8a34932102951b25870966c61d06d6b97156)

Deletion Driven Development: Code to delete code!

Video in TIB AV-Portal: Deletion Driven Development: Code to delete code!

Formal Metadata

Deletion Driven Development: Code to delete code!
Title of Series
Number of Parts
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this license.
Release Date
Confreaks, LLC
Production Place

Content Metadata

Subject Area
Good news! Ruby is a successful and mature programming language with a wealth of libraries and legacy applications that have been contributed to for many years. The bad news: Those projects might contain a large amount of useless, unused code which adds needless complexity and confuses new developers. In this talk I'll explain how to build a static analysis tool to help you find unused code and remove it - because there's no code that's easier to maintain than no code at all!
Hill differential equation Lattice (order) Machine code Twitter Arc (geometry)
Inheritance (object-oriented programming) Different (Kate Ryan album) State of matter Bit
Twin prime Forest Metropolitan area network Exception handling
Enterprise architecture Functional programming Programming language Email Touchscreen Open source Multiplication sign Line (geometry) Machine code Cartesian coordinate system Software maintenance Power (physics) Latent heat Goodness of fit Data management Term (mathematics) Personal digital assistant Point cloud Software framework Office suite Computing platform Form (programming)
Complex (psychology) Group action Dynamical system Parsing State of matter Multiplication sign Combinational logic Set (mathematics) Parsing Mereology Machine code Computer programming Fluid statics Coefficient of determination Positional notation Spherical cap Diagram Extension (kinesiology) Error message Factorization Formal grammar Social class Area Programming language Parsing Algorithm Context-free grammar Building Computer Bit Mereology Instance (computer science) Term (mathematics) Sequence Parsing Abstract syntax tree Derivation (linguistics) Radical (chemistry) Type theory Latent heat Arithmetic mean Convex hull Text editor Right angle Quicksort Reading (process) Point (geometry) Stapeldatei Slide rule Implementation Inheritance (object-oriented programming) Regulärer Ausdruck <Textverarbeitung> Programming language Computer file Software developer Token ring Syntaxbaum Virtual machine Abstract syntax tree Rule of inference Sequence Term (mathematics) Reduction of order Representation (politics) Directed set Data structure Form (programming) Rule of inference Context awareness Noise (electronics) Validity (statistics) Poisson-Klammer Electronic program guide Projective plane Expression Operator (mathematics) Machine code Predicate (grammar) Software maintenance Symbol table Symbol table Word Positional notation Software Predicate (grammar) String (computer science) Formal grammar Codec Control flow graph Form (programming)
Context awareness Group action Parsing Parsing Sign (mathematics) Computer configuration Different (Kate Ryan album) Personal digital assistant Social class Exception handling Mapping Block (periodic table) Building Electronic mailing list Bit Mereology Instance (computer science) Electronic signature Message passing Process (computing) Hash function Problemorientierte Programmiersprache Quicksort Spacetime Point (geometry) Inheritance (object-oriented programming) Computer file Cellular automaton Syntaxbaum Shift operator Number Ranking Data structure Traffic reporting Dean number Data type Compact space Default (computer science) Information Demo (music) Key (cryptography) Interface (computing) Exponential function Line (geometry) Coprocessor System call Symbol table Mathematics Uniform resource locator Word Personal digital assistant Network topology Social class Logische Programmiersprache Chord (peer-to-peer) System call Length Interior (topology) Multiplication sign 1 (number) Set (mathematics) Function (mathematics) Parameter (computer programming) Stack (abstract data type) Mereology Electronic signature Machine code Coefficient of determination Positional notation Befehlsprozessor Hash function Process (computing) Endliche Modelltheorie Parsing Computer virus File format Complex (psychology) Menu (computing) Parsing Type theory Block (periodic table) Row (database) Ocean current Trail Game controller Perfect group Coprocessor Graph coloring Attribute grammar Revision control Object-oriented programming Natural number String (computer science) Default (computer science) Shift operator Inheritance (object-oriented programming) Validity (statistics) Expression Mathematical analysis Planning Stack (abstract data type) Machine code Pointer (computer programming) Vertex (graph theory) Formal grammar Key (cryptography) Traffic reporting
Web crawler Context awareness System call Parsing Multiplication sign 1 (number) Combinational logic Set (mathematics) Parsing Parameter (computer programming) Machine code Roundness (object) Computer configuration Hash function Core dump Pattern language Process (computing) Endliche Modelltheorie Partition (number theory) Social class Programming language Parsing Gradient Shared memory Virtualization Parsing Entire function Hand fan Partition (number theory) Type theory Numeral (linguistics) Process (computing) Computer configuration Hash function Internet service provider MiniDisc Pattern language Right angle Problemorientierte Programmiersprache Quicksort Row (database) Asynchronous Transfer Mode Spacetime Point (geometry) Trail Implementation Computer file Content (media) Focus (optics) Coprocessor Attribute grammar Latent heat Authorization Integer MiniDisc Form (programming) Expression Projective plane Counting Database Machine code System call Symbol table Single-precision floating-point format Personal digital assistant Key (cryptography) Table (information) Library (computing) Traffic reporting
Message passing Machine code Mereology Machine code Software bug
Coma Berenices
so welcome to a talk that I like to call
deletion driven development my name is Chris Arkin here's what they look like on Twitter and github I'm a really social person and love meeting new friends the conferences and whatnot so be sure to say hello my username everywhere is just Chris arc and so we
are here in Cincinnati Ohio at the lovely city of Cincinnati I've never been here before but I'm really enjoying the week especially the giant plates of cheese with a little bit of chili that they put in them it's really good I hailed northwest of here up in Minnesota on the Canadian border Minnesota goes by a bunch of different names you've probably heard of before
one is the land of 10,000 lakes there's the North Star State that's super cold place if you're from Canada you might know it as the great white south and if you're at a ruby conference like this one you might vaguely remember it as
that one place for those JRuby guys live right so like these two guys I live in
the Twin Cities of Minneapolis and st. Paul we have absolutely gorgeous summers there and have beautiful forests and lakes to enjoy in the winter we love playing hockey and the winners always look as beautiful as they do in that bottom picture except if you've been there you know that I'm lying it often
looks a lot like this that's a man on a snow bicycle riding through a blizzard after such a blizzard sometimes things
look a lot like this this is a line of cars parked on the street if you can't see but hey I'm just gonna repeat that
the summers are lovely and you should at least come visit during the summer sometime if you're nearby so I'm a ruby developer at what Aaron Patterson has always described as a small start-up you
might have heard of us called red hats I work remotely out of Minnesota there's no engineering office there and at Red
Hat I work on manage IQ so manage IQ is an open-source cloud management platform that powers Red Hat cloud forms downstream it basically aggregates all of your enterprise infrastructure into one place and adds a bunch of functionality on top of it the code base is hosted on github it's easy to find and you can learn even more about it's talking with me afterward or at manage IQ org and we're always on the lookout for good developers if you're interested in joining us please see me send me an email whatever I also have a ridiculous amount of swag with me at this conference so if you feel like you don't have enough stickers or shirts or lanyards or screen cloths or even manage like cute candies please seek me out here at the conference so why am I here I am here because I love programming and as such you can probably imagine that I love writing code however there's something else I love even more and that is I love deleting code so ruby has been a successful programming language for some time now and we as ruie developers might now maintain legacy applications that have been developed on for many years a consequence of our long-term success is that these applications may contain unused obsolete and unnecessary code now I'm going to tackle a specific case today I'm going to talk about methods that stand worthless and dead unused by any callers in the application and that might be fine for frameworks where public API is exposed and never called within the framework itself but in terms of an application it just adds cruft now how does code like this end up in our
projects there's a couple of reasons that I can think of think of like a developer from the beginning of the project years ago adds methods that they think will be useful someday but actually aren't they never actually get used the implementation might change underneath them and they don't actually work anymore that's a kind of over engineering there's a little poorly written code so imagine a brand new and experienced developer joins the project which is great they write a very specific method that isn't very flexible and is completely unhelpful beyond anything besides the single spot that it's used maybe it could be refactored and written a little better more generally now hopefully these two examples are just caught in code review but sometimes they aren't it also could be that things have just been refactored over time and methods just aren't needed anymore so you might ask who cares the short answer is that unnecessary code is confusing it adds complexity where there shouldn't be any and it creates an unnecessary maintenance burden on future developers and it makes you scroll more on your text editor which is annoying but don't just take my word for it other people think so too there's a great post by Ned Batchelder called the leading code from 2002 and in this post there's a snippet that I'd like to share if you have more if you have a chunk of code you don't need anymore there's one big reason to delete it for real rather than leaving it in a disabled state to reduce noise and uncertainty some of the worst enemies a developer has are noise or uncertainty in his code because they prevent him from working with it effectively in the future now before we get wound up trying to implement a feature and already lost in the noise and uncertainty I ask what
if we could programmatically find unused code to delete ahead of time before we're trying to implement something else in that area of code it turns out we can to an extent so today I'm gonna describe how a static code analyzer can be built to find potentially uncalled methods now because Ruby is a dynamic duck type language and this analyzer is only static it's not going to be a hundred percent accurate but it has the potential to point out some areas in our code that we can clear out some cruft and add a bunch of deletions to our github stats so we start with some Ruby code that we want to analyze the first thing we need to do is transform the code into some data structure that we can reason with which brings us to part one parsing the code now some of you know how language parsing works and are very very aware of how it works with Ruby some of you may be aren't and I think it's important for everyone to understand how things work from the ground up so this is really a high-level overview of how general language parsing works from a grammar how Ruby does it and how we're going to do it so do you understand the following sequences of characters and how do you know the boy owns a dog okay a boy bites the dog kind of weird but okay loves boy though now how could you programmatically determine which of those are correct and which are not there's a way to do it and I'm going to throw a couple definitions that might be very familiar to you one is a context-free grammar or a CFG it's a set of rules that describe everything contained within the language it basically answers the question what sentences are in the language and what or not we also have backus-naur form or BNF this is just one of the two main notation techniques for describing this CFG so here is a context-free grammar for all of the sequences of characters that we showed you earlier it's really simple to look at everything you see on the slide here is a symbol and symbols are split into two groups the non terminals with the little brackets there and the terminals that I'll put as all caps the way it works is a symbol on the left is replaced with an expression on the right an expression can be a combination of non terminals of terminals and a non terminal is always replaced via some rule now terminals are the actual token found within the language they terminate at that point that's why they're called terminals so let's look at that first example the boy owns a dog now notice I didn't say sentences I said sequences of characters we don't actually know that this set of characters up here is an actual sentence but we have a rule for it and we can try to apply it now if this thing is truly a sentence that means it has to be a subject followed by a predicate at least within our simple grammar now if we try and split it out and say the boy is the subject and owns a dog as a predicate we can keep following the rules by replacement so if the boy is truly a subject a valid subject it has to be an article followed by a noun and again we'll go through and see all right let's try an article is the and noun is boy again we go through we see the rule for article it has to be the terminal the or a which it is doing the other side there's boy or dog terminals found it parses correctly you can also do the other side parsing down until you find those terminals so in the end we've identified every part of what we call a sentence in our grammar and if we do a little bit of rearranging around we can see this this is a parse tree it's the discrete representation of our language that we can use to reason about the sentence what about a boy bites the dog again if you go through it all it has the same sort of structure so it parses out correctly it's totally fine but do boys often bite dogs maybe could happen seems a little weird we'll come back to that love's boy though as you can imagine this one doesn't work out there's a reason why well if this is a sentence that has to begin with a subject if that's a subject it has to begin with an article and an article isn't valid if it's anything but the or a it's a syntax error it doesn't belong in the language so in programming terms the written sentences or the code we're talking about here equate to these conclusions the boy owns a dog makes sense a boy bites the dog is technically correct it's maybe not what we meant though in software terms that could be a software buck right you write Ruby you don't have a syntax error but it might be the wrong thing well maybe a boy bites the dog maybe he doesn't I don't know and then love's boy though as a syntax error it doesn't work so what does this all have to do with Ruby well you can ask Ruby the same thing how does Ruby know the meaning of these characters how does Ruby know that this is a class definition named person that it has two methods initialized and say hello there's an instance variable named etc etc well Ruby does the same thing that we did my English examples were easy because we skipped lexing tokenization and actual programmatic parsing we just kind of win on intuition saying this looks like a subject but hopefully it captured the high-level essence of parse trees for you it's a bit more complex and how ruby actually accomplished with it here's what C Ruby does so ruby has the infamous parse Y grammar file which it gives to bison the resulting parser code is used to scan through your Ruby files and token eise's them then parses the tokens into an abstract syntax tree which is then compiled to instructions for the virtual machine now the parser generated from bison is what's called an lalr 1 parser and i'm not going to describe how in lalr parser works for you today because it's a bit out of scope however i'm gonna plug this very excellent book so the diagram I just showed you is from this book called Ruby under a microscope it's by a fantastic human being by the name of Pat Shaughnessy in it he explains all about ruby internals the first chapter is all about tokenization and parsing that I just showed you including an in-depth explanation of the parse algorithm so it's early fascinating read you should definitely go check it out so how are we gonna do
it we're gonna do something a little different we're gonna use a gem called Ruby parser which is a ruby parser written in Ruby using rack rack is an lalr 1 parser generator so let's take a look at example we'll have a class named person with a method greet that takes in a name and just says hello name so you can initialize a new person and say hello / it becomes so Ruby parser has a class method called for current Ruby that brings an instance of Ruby parser for a grammar for the current running version of Ruby which you can then feed it the parse method to that and it gives you back this now this is an S expression or a sex B it's a notation for nested list data and it originally comes from Lisp now nested list data is tree structured so it's the perfect notation for describing parse trees so awesome this data contains the structure of our code you can see the block node up here is the top-level context with a class name person within that as a definition node name greet that takes an argument name etc so now that we have a parser to put our code in a format we can work with we now need a way to process it which brings us to part two processing the S expression so before we do something really useful with our sexby we need a way to easily manipulate it and start getting some general information that we care about information like finding where exactly methods are defined and in what classes we're going to begin to process everything by building a very minimal tiny class called minimal sexby processor and the goal of this class is simply to run dispatch calling a method given a node type in our s expression if it exists so when our initialized method will build a sort of dispatch hash we'll take the public methods in this interface find all that start with the prefix process underscore and key them within a hash according to their suffix that is if we had a method named process definition we would seek out the method from its prefix take the suffix and place the method name as a symbol within the processors hash key by the suffix note the name corresponds to a node type in our sexby next we'll write the main method for our processor every node in the tree will be passed into this method it simply looks into the processor hash to call the correct processor method given the current expressions node type if there isn't one we call a default method that we can set as an option if we don't have a method to call and didn't set a default let's just return nil also we'll put a cute little warning output to say we didn't recognize the node type and are calling the default method if that's actually what we're doing now this class if you noticed is pretty worthless there are no processors it's simply a base class so to demonstrate an example of a process or subclass I'm gonna define a subclass called silly processor within our silly processor we'll define two processor methods if we encounter a method definition in the expression process definition will be called process not definition will be the method that we'll set as the default for all other node types so the methods just call puts to tell us information about the nodes you can see if we encounter a method definition node will say processing a method definition node and all caps and for the everything else we'll just say here's the node type so both of these methods call some method called process until empty now process until empty iteratively shifts and calls process on the next node in the expression until the expression is empty every processor method calls this in the end to start parsing at the next node lastly we'll fill our initialize method to call super and the parent first and set the options that we want so we'll say hey if you don't understand you don't have a processor for this current node we'll call process not definition and we're going to turn off warnings because we expect most of the nodes probably won't be identified it looks like that so we'll put together a little demo again we'll just get the expression from Ruby parser and then call process on that and we can see this is what it looks like it's about what do you expect it goes through finds all the nodes and for the method definition node it does something different now you might be thinking wow that's pointless that's because it is but we've now added the next tool to our tool belt now we're at the point that we can run whatever code we want at a given node and this allows us to build more complex things like say
a method tracking processor so using some information from Ruby parser we can now record where we see method definitions in classes and their line number we'll set up this class with the same options as our silly processor but we have a couple new things we have two new stacks a method and a class stack we're gonna use these to keep track of where we are in the code or the tree we also have a method locations hash that will populate with the method signature as keys and the file name and line number as values now the file and line number will be taken directly from Ruby parser we'll define a couple processor methods so process definition will just shift off the node type on the expression the next thing will be the name and then we'll call this in method that gives a block and this process within there so this doesn't do much other than signify we're in a method by calling a method class does the same thing so process class will call in class and then the process until empty is the same thing that you saw before here are the two location methods that actually do the work now this might be a little hard to see but it's very simple they both just add the current method or class on to their respective stacks and pop them off once we yield to the block passed in within method we also record the current method signature in our method locations hash with its location so another thing to think about is that if you enter a new class that's a new method space different methods could be in that context so we save the old method stack when we go into a class use a new one for that class and then revert back to the old stack when we're done processing it lastly a couple little helpers so the current class name would be the first thing on the class stack the current method name would be the first thing on the method stack and then we'll record a signature that you're used to seeing with a class name with a little hash and the method name great so let's expand on our example a little bit we have a person with greets we're gonna add a little say goodbye which does magically almost the same thing we have a class called dog with a bark method that whoops and then we're only going to call greet there the important thing to pay attention to is where the definitions are defined right so we have greet on - we have a method on 6 and 12 so if we do the same thing as before and prettyprint the method locations you can see that we found person greet on to say goodbye on 6 and bark on 12 perfect so awesome we now know where methods are defined the generic processors that we've built so far to process the s expression tree and record method locations provide the footing with which we can build our to long the only thing we need to do now is process the call nodes within the tree and see what's being called and lined them up with what Colley's were now tracking which brings us to part three building the dead method finder yes we finally reached the point where we can build the dead method finder that we've been working towards so we'll do that that method finder will subclass from method tracking processor so in this one there are two important collections that will maintain known is a hash containing set so we'll use this to maintain a mapping of method names to the set of classes that define them that also will be called and this will just be a set of methods that were called here are to processor methods so for process definition we're gonna key into that known hash adding the current class name to being processed to the set of classes that call this method then process then call process until empty on the remaining sexby nodes with process call will add the method being called to the set of called methods next we'll define an uncalled method this is where we'll take the difference between known and called methods in other words the ones that weren't called and for each uncalled method we then key into the known hash to find where a method by that name is defined by class and line number we also have a little helper method called plane method name this is just because the method name from be parser is a string but we're gonna use symbols it looks like that so let's expand on our example once again so with greet and say goodbye instead of calling puts directly let's have a little helper called speak that does it for us we'll also have a pet dog in person that takes in a dog object and sends it pet and with dog we'll add a little attribute accessor called fed because hey maybe you want to keep track of whether or not you fed the dog and we'll add a pet method as well so to add a little bit of realism I have a dog named Ruben so I will call myself the person that knew and my dog there I'll greet Ruby Kagi our fruity cakey Ruby kampf Ruben will bark and then I will pet Ruby so we process the sexby and then we puts out the uncalled and we see person say goodbye is supposedly uncalled and dog pet is supposedly uncalled now if you're looking closely you'll find that this is wrong there's a problem here supposedly on line 38 I pet Ruben but for not finding that that's actually called now why is that it's because we fit an edge case right the problem is that our process call is using send as the method being called which it is really it is using the method sent but we want to take into account that sending implies a method call directly in other words we've hit an edge case so looking at our s expression for that we can find where the actual method being called is and add some logic to say that that is the method being called and handle the edge case we'll say hey when the method being called a send public send or underscore underscore send look through that find the literal being sent and we'll say that that is the method being called let's try it again great that's there that's being called so it's no longer found in our output now this is improvement but it's still not correct about this guy here we never used this attribute accessor fed right so it should be in the output looking at our s expression for that we realized that Adar XS ER is itself a method called that defines methods right a getter and a setter which is why our method tracking processor doesn't find them let's add another case in our color processor to handle that say hey when you encounter attribute accessor go through and record that as a known method now record known method does the same thing that we were doing in our process definition method except it also double checks that the method location was recorded well we're here tinkering that's also pretty up that output and it's something that's a little more readable let's define a method called report that looks through the uncalled methods for each of them let's look at the location of that method via method locations in our parent class throw all of that into a pretty formatted lens report and skip this class if there are no uncalled methods if there are we join it all together and we print it out processing the code now is just getting the s expression processing it and calling report so here's what it looks like after we fixed the outer accessor case in printed up our output you can see that awesome say goodbye is supposedly uncalled which is not and our attribute accessor is not used either which is not awesome so great perfect awesome now remember that this static analysis is not always 100% accurate and that these are potentially uncalled so we do some manual checking ourselves to be sure that these actually are deletable so I suspected it looks like they are deletable in this very easy example so we delete them doesn't it feel great isn't the leading code fun it's perfect so hey we did it we made a ded method finder now we can start finding code to potentially delete left and right yes time to open a dozen pull requests with a bunch of red deletions right we truly done no we are Ruby is complex to parse and Ruby has a lot of edge cases but the good news is adding edge cases is easy so think about for example Rueben dot fed equals true if we actually did use that accessor if we add that to the code here you'll notice that it's still marked as uncalled here now if my dog were here and saw him that being fed was an issue he would probably say just deal with it and that is an actual picture of my dog wearing sunglasses well back to the edge case so it's a natural sign node which means attribute assignment the variable Rubin has the message fed equals sent to it with the value of true so we can record that again as mythical perfect looks great there's so many other edge cases though what about rails methods so every bit of rails DSL and controllers and models that you're used to using would be an edge case and there's a lot of them right there's after commit there's before create there's after create there's before update there's before destroy there's before filter except it's not called before filter now now it's before action around save validate validates validates length of after validation thought its formative alt its cuteness of all these confirmations yeah you could and what about my own DSL so
in manage IQ we actually have our own virtual column implementation for an active record now it's digs deep into active record internals to allow us to treat any method as a database column amongst other things it's mainly used for reporting purposes reporting attributes of entire tables with extra attributes sprinkled in so for example you could have a class disk that has many partitions and you might define a virtual column called allocated space with a type of integer that uses those partitions so we actually add a DSL to Rails models in the form of these virtual column calls the point here though is that this DSL and rails DSL calls aren't that difficult to handle it's just another edge case all of these methods look essentially the same most of the time it's arguments of symbols and naming methods to be called so we can go in and call them all basically the point that I'm trying to make here with all these little edge cases is that as with most things with the right tools the job isn't very difficult and customization is easy the other thing that's easy is that you can execute this code on your project right now so there is a ruby gem called debride now the author of this gem told me last night that is pronounced debride but apparently Gorby puffs eye doctor proclaims it as debride so we'll go with that duda pride something is to remove dead contaminated or adherent tissue and/or material which sounds a lot like deleting useless code so when I first thought of programmatically finding dead code to delete I went down the exact same path we just did starting with rack and Ruby parser I then discovered the lovely simplicity of processing s expressions with a gem called sexby processor it was created to easily do generic processing of s expressions given by Ruby parser it also provides a method based sexby processor subclass to the method and class tracking that we did with our method tracking processor today then to my great delight Ida stumbled on debride which does exactly what we did today with our dead method finder what's more is that everything you see here on the stack from Ruby parser on is written by the same person each one of these projects is written by Ryan Davis obviously a ruby grade and you people are in serious luck because mr. Ryan Davis is here at this conference in fact Ryan are you here in this room there you are can I get a round of applause for Ryan for all the fantastic thank you so I've been hacking on debride off and on for the past several months customizing it for manage IQ and finding crufty code to delete on a project that started on Rails 1.2.3 nearly a decade ago and thought it would be fun to rebuild the basic concepts for you today all of the code you've seen is a modified and minimalist example of Xen spiders very excellent work so what does your bride provide that we haven't covered today well it covers more edge cases our simple method tracking processor and ded method finder of the core of what the bride does but there's so much more to consider what about finding methods and singleton classes what about numerous other uncommon Ruby syntax like calling methods with colon colon all those little edge cases Ruby is a crazy flexible language and there are many cases yet to be handled the bride also adds all sorts of lovely little options like excluding particular files white listing methods based on a pattern focusing on a particular path there is a rails mode just like the one you saw earlier so I mentioned I've been hacking on your bride to find even more dead code now besides adding your own DSL white listening patterns and all that you can easily do something like this to find other criteria from what might be deletable code so remember what I said about methods that are maybe way too context specific that might have only one caller here's a really hacky way to find those methods instead of keeping a set of called methods we can keep a hash with every value being a call count then every time we counter a call we just increment that count and then the ones that are called once are the ones that are called once perfect so those are again cases where a method might not even really need to exist maybe there's something that's way too specific that the call caller itself could actually just handle it there just depends on you know what you're doing so I've been busy enough hacking into leading code that although I've opened a couple little things in the project I've still got plenty more that I want to refine and then share so pushing more work upstream it's definitely my future consideration yes please
Ryan says yes please so remember tools like this are but one tool in the toolbox when finding ways to clean up your codebase today we looked at one-way parsing and statically analyzing Ruby itself to find potentially uncalled code but there's so many more awesome tools to use in combination for example there's an old code finder by Tom Coplin which is a ruby gem that basically checks code content by date and authorship it can get so maybe you have someone named Fred who used to work at the company and doesn't anymore for many many years well you might want to take a look at his code specifically because there might be more stuff that you can get rid of there's also unused by Josh Clayton over at thoughtbot it's written in Haskell it utilizes C tags to statically find unused code so it's not particularly strapped to one programming language and I am a huge huge fan of using C tags I've not looked into that yet but I really really want to someone mentioned I think last night there's also a library called scythe I haven't heard of it I see a couple nods though that is also another one that you should check out that I forgot that so before I
go here's a parting message for you and if you use merb before it was merged into rails you might recognize this no code is faster has fewer bugs is easier to understand and is more maintainable than no code at all so when you go home from this awesome conference delete some
code it feels fantastic thank you