Hacker News new | past | comments | ask | show | jobs | submit login
One of the Best Bits of Programming Advice I ever Got (objology.blogspot.com)
218 points by apgwoz on Sept 26, 2011 | hide | past | web | favorite | 86 comments

I am unconvinced. Any absolute deserves close scrutiny, and "real nouns only" is an absolute. In this case, it fails my test. I find that many things I work with (especially huge Java libraries) tend to have convoluted and difficult-to-understand webs of classes with the most confusing pieces named things like "FooManager" or "FooController". So the author of this piece is reacting to a genuine excess.

But Travis is overreacting. The REAL goal is to find a collection of abstractions that is powerful enough to accomplish what the program requires but as simple as possible (and as local as possible) to allow humans to comprehend (and maintain) it. There are quite a few times when I have found that one of these "verb-like" abstractions (a word ending in "-er") made things more comprehensible.

For example: the Gang-of-Four patterns "Builder" and "Observer" both end in "-er" and strike me as being well-named. "Builder" is an object with no purpose other than to create (initialize) another object or data structure. HAVING such an object is useful when something requires extensive setup or initialization, because it is easier to understand if the extensive initialization is kept separate from the core functionality. "Observer" is the name for an interface of things that observe... the names of interfaces FREQUENTLY work well as "verbs" since the interface frequently represents "anything that does X".

Similarly, I have often had a class such as "DatabaseConnection" and then created "DatabaseConnectionWrapper" -- a class whose purpose is to wrap a DatabaseConnection to do something like logging, error reporting, pool flushing, driver-bug-patching, and so forth. Suggestions that I call these "LoggingDatabaseConnection", "ErrorReportingDatabaseConnection" and so forth misses the key point: a different mix of features may be required at different times (eg: logging in QA but not in Prod); the fact that they are transparent wrappers that can be added (or not) in any order is a key feature of the design.

So my own advice would be weaker than Travis'. Instead of "Don't make objects that end with -er.", I would say "Be wary of confusing objects; those ending in -er are often confusing."

I think there is a more fundamental misunderstanding in OO than bad naming convention. You are right about the usefulness of -er named objects -- but then again, are they really objects, or just functionality wrapped into a wrongly chosen language construct?

Following your example, you could have implemented the strategy pattern into DatabaseConnection to freely inject functionality into the object when X happens. Assuming these functions do not have additional data members other than the parent object, they easily can be simple functions or even lambdas.

(Of course it's easier to restrict to classes, because you can validate against interfaces in case a function signature doesn't cut it.)

What Travis is suggesting, when you have an object that defines both data and behavior, the object context should be determined by the data, not the behavior. If done properly, you end up with less names that end in -er.

There are many times when the objects really do have both data and behavior -- glancing at the real code that was the basis of my example, I see that the LoggingDatabaseConnection has real state (the place to log to) and the pool flushing one has real state (keeps track of open cursors so they can be auto-closed when it is returned to the pool).

Of course, you also could be (and I was) using a deficient language like Java which doesn't HAVE any way to use a function or lambda. But I agree with you: a simple lambda a better solution where it is available.

And this kind of use is quite far from what Alan Kay intended when he designed SmallTalk. The original idea was that the objects would correspond to real-world entities. I would contend that since then OOP has evolved, and now the key idea of an object is the pairing of data and methods. Choosing the abstractions which will make the code most readable is one of the great challenges of programming well, and I find that just going for the nouns every time is NOT the best answer.

Most OO examples talk about commonly known external objects; yet programmers need to invent classes for objects too, because they are inventing and classifying an unclassified world. So they create Drivers, Managers etc; I agree with Travis to certain degree, but on the abstract object creation sometimes objects are identified by behavior rather than data; especially data manipulating objects; queue managers, drives ...

I sometimes think human language is not enough for programmers, and there must be an easier way to create non-existing objects; maybe similar to German language word creation.

>The REAL goal is to find a collection of abstractions that is powerful enough to accomplish what the program requires but as simple as possible (and as local as possible) to allow humans to comprehend (and maintain) it.

I like to think that practicing OOP in some sense is a process of defining new language and then using this language to programs something. This can be said about almost any programming in general, but in OOP you can see bad abstractions very well.

The real problem of OOP is that most "average" programmers are bad at defining languages. From my unscientific observations in any large C++ project only few developers actually produce usable class hierarchies. Same applies to large Java projects to somewhat lesser degree - Java has better defined patterns and more extensive standard library.

I disagree with this advice and the reason I disagree with it because it completely disregards the human tendency to name objects (not OOP objects, but real-life objects) based on their functionality. Guess what a photocopier does? Or a scanner? An eraser? An elevator? I don't think these are particularly bad names; quite the contrary, their meaning is very clear.

Why would it be any different for OOP objects? If there's an object with an internal state that chiefly responds to messages about scanning things, what's wrong with calling it NSScanner?

I think in the author’s ideal world, we would not have Photocopier, Scanner, and Eraser objects, but rather a Paper object that can copy, scan, and erase itself.

Yeah, but good luck trying to reuse the same basic instructions to scan non-paper objects without multiple inheritance.

See: Mixins, Traits, Apects. Whatever your language wants to call them, they're fundamentally the same, and solve the issue you're raising.

Ruby does this with mixin modules :)

     interface ICopyable {...}

Back in the days at PARC, that was "IXeroxable".

I was going to write basically the same thing. I used to combine data and action in the same classes but recently have begun refactoring them out into separate data and "-er" classes. Maybe somewhere an OOP purist is frowning, but I really prefer this naming convention. My FlowchartShape class included methods for formatting and positioning. I now have a FlowchartShapeFormatter class and a FlowchartShapePositioner class. Guess what they do?

Agreed. The reason I end up with some '-er' classes is to separate out immutable definitions and the classes that work on the definitions. In your example, by making the FlowchartShape a simple definition class, you can then write that to a file or send it over the network as a snapshot of the state of the -er classes.

If the article's advice was taken, you'd end up renaming WebServer to be WebPage, but then would have nothing to name a WebPage. (Maybe WebPageSnapshot?)

It doesn't seem like it'd be any more difficult to write the FlowchartShape to a file or send it across the network if it had additional methods on it to manipulate and use its state, so I'm not sure what's gained by making two+ separate classes... what am I missing?

    what am I missing?
That it's quite possible that you don't want the guy on the other side of the wire to call those methods on the object. I find I quite frequently want to share a data model between my client and server code, but I certainly wouldn't want to expose all the stuff that the server does to those objects to the client directly.

In my experience, it seems like the vast majority of the time in which that would be the case, one isn't doing a native ("binary") object serialization such as would require the receiver to have the object code for the serialized class(es), but instead using some sort of intermediate format like JSON or XML or the like. But I suppose there might be instances... I just hate to throw away such a fundamental OOP concept as bundling of data and the methods that operate on that data unless there's a really good reason.

He does say, at the end of the piece, that nouns that happen to end in -er are an exception.

Aren't all words which end in -er, and used as class names, nouns ?

Sorry. Meant to upvote you, but downvoted by mistake.

> Guess what a photocopier does? Or a scanner? An eraser? An elevator?

Or a Coffee-Maker... Instant factory pattern.

Amazing how small linguistic changes can cause large semantic shifts.

Comparable to the post's rule: in English, a few rules I've picked up are

- avoid words ending in "ly" (suffix weakens concepts; suggestion from Stephen King)

- never start a sentence with "I" and otherwise minimize its use

- "but" negates everything that came before

- avoid "to be"

Any other such suggestions, in natural or artificial languages?

Good code isn't written, it's re-written.

Omit needless code. Omit needless code. Omit needless code.

George Orwell's five rules of effective writing come to mind: http://www.pickthebrain.com/blog/george-orwells-5-rules-for-...

And people so often substitute longer words, (e.g. 'myself' instead of 'me') when they want to sound professional.

"I hit myself" => OK "Please contact myself" => WRONG.

"I can' let you do that, Dave"

There are plenty of times to use "I" at the start of a sentence. Told by my boss "we're out of power converters", it's natural and correct to simply say/write "I will get some at Toshi station on the way in". Trying to find a sentence to say the same thing without starting with "I" is unnecessarily torturous.

Is there a collection of suggestions from Stephen King? I love picking up small pieces of advice from respectable writers, and he is undoubtedly one of them.

Stephen King's On Writing: A Memoir of the Craft.

Most of the book is his less-than-riveting autobiography.

The ~16 pages which are in fact about writing ("Toolbox") are worth more than the cover price.

Someone else mentioned Stephen King's (enjoyable) book. If you're after more "small bits of advice from respectable writers" you should look into Lawrence Block's books, specifically "Telling Lies for Fun and Profit" which I keep coming back to whenever I'm stuck.

> Lawrence Block

My favourite crime author, by a long way. Who else can write about a killer and make him a sympathetic character.

Who else can write about a killer and make him a sympathetic character.

That's easy, Donald Westlake writing as Richard Stark. Though Block is probably my favorite crime author as well.

Avoid the passive voice.

Geoffrey K Pullum has a delightful article tearing Strunk and White's prescription and examples to pieces. While there's some truth to the advice, it is often simplistically presented and ludicrously overapplied (uh, I mean, "style guides often present it simplistically, and editors often overapply it"). There's plenty of room for the passive voice, and even more so when you consider what Strunk and White consider to be passive even though it isn't.


whats humorous is that Strunk and White use the passive voice in their recommendation to not use it!

There are reasons to use variants of the passive voice. Using Wikipedia's NPOV [1] approach with speaking with people is very useful in attempting to agree on fact first so emotion is avoided for charged situations/discussions.

[1] https://secure.wikimedia.org/wikipedia/en/wiki/Wikipedia:Neu...

Scientific reporting also benefits greatly from use of the passive voice.

- read Strunk & White.


I'm working on a video game at the moment, and have read lots of advice like this. My old game engine experiments all used 'Entities' to describe objects in the game world, and these 'Entities' were created, deleted and invoked by an 'EntityManager' (kind of half factory & half controller). Every game tick, the EntityManager calls a 'tick' method on all the current game entities and facilitates communication between them. For the most part, all entities are created/destroyed at the same time (game-level loading/unloading).

Trying to avoid architecture mistakes, I've come up with all sorts of tortured designs to do away with the concept of any kind of 'manager'. It is probably my inexperience with various design patterns, but all these alternatives were overly complicated and difficult to use. I would end up with a mess of objects & functors all needing pointers to each other & stepping on each others toes.

It then occurred to me that my 'manager' was in fact mapped to the real-world concept of 'a manager', being someone/something that is given orders from upper-management (game-level data, user-input, etc.) 'hires' & then assigns tasks to 'workers', hands the results to upper management, then 'fires' the workers when they are not needed ;). The metaphor works.

So I'm back to the 'manager' pattern and things are moving ahead quite nicely, even if some OO purists might frown. Should I call my 'EntityManager' 'EntityBoss'? Gets rid of the 'er' at least...

Similarly, my game has Managers for entities, collision components, and renderable components. With each one of these, I don't want to try to dump all of the data in one place where the access patterns become strained, I want them to be "managed" from afar, where there's an interface to easily query or update them.

Yeah, from my own experience the 'manager' metaphor is just easier for understanding the flow of execution & adding-new-features/maintenance.

Everything is in one place, and when the systems do need to communicate it is usually in pretty well defined ways (eg. physics manager ticks--> passes collision data to entity manager which ticks--> passes scene data to render manager which draws the screen, etc)

I'll go with whatever pattern works best in the name of Just Getting Stuff Done (with my own limited brainpower).

The entity boss part seems like it's just an array of objects you perform actions on. It's not a description of the goal of the object, it's an internals-level view of how it does it.

Maybe there's another more fundamental behavior or dataset that would suggest a better name?

Perhaps Scene or World, depending on the things the EntityManager was doing. If it's reloading them it's like a scene, if it's changing gravity it's more worldy...

I generally accept the point made in the article, but what should I use in place of "Controller"? If using MVC I generally call it "ApplicationController" or "SimpleController". Should it be "Application", "ApplicationControl"...?

From the OP: "The fact that every single MVC implementation in the world has had a different role for Controller ought to tell us something about how well that idea fit."

We were just discussing this yesterday: http://news.ycombinator.com/item?id=3036153.

A favorite principle: when you can't find a good name for something, it's often a sign that the concept you're trying to name isn't appropriate.

What I still don't understand is, what would I put in place of the Controller? Events are raised by the user, these are all fired off and I want to handle them in the same place in the same way.

I certainly don't want event handling code all over the application just because this means I can then have noun-only classes?

what do the controllers actually do? Synchronize state? Verify that constraints are met?

I believe the OA's intended question is what are the controllers? Well, what are they? Yes, I know they control things, but what is the actual thing that they are?

Arcade games are a good analogy for MVC. The controller is the joystick, buttons, etc. The view is the CRT screen. The model represents the game state.

You're suggesting I call them "StateSynchronizer" or "ConstraintVerifier"?

No, I suggest you name it after what it actually does. "Controller" is a role that's only defined in terms of what it interacts with. It's essentially a declaration of rank with other types; it doesn't say anything about what functionality the class provides.

If I'm looking at the components of the software, and I want some code to do X, or wonder what a class Y does, what does "Controller" tell me?

If you're doing constraint verification (for example), my preference is to have a container (e.g. vector<>) of Constraint and just run find_if() to see when Constraint::Valid(environment) returns false. Then it's a class called ConstraintVerificationUtil and it has a single static method: VerifyList.

What do you do if you need a higher level interface to a bunch of classes that are already fairly complex in their own right? Seems to me "plain" nouns (since really these are all nouns) are good for simple concepts and things with real world analogs, but in projects of any complexity you quickly end up with complex logic whose only succinct description is what it does.

My experience tells me the key aspect of software quality is the interfaces. The simpler and less leaky the interfaces, the more understandable, maintainable and correct the software will tend to be. Part of this is inherent to the problem itself, but a big chunk of it—especially in the middleware—is the ability of a software architect to have a sense of the whole project and figure out the best places to carve the interfaces. Doing so correctly is the difference between a maintainable million-line project and spaghetti. Truly brilliant engineers will figure out interfaces that make code 100x more reusable and elegant, maybe spawning open source extractions, etc.

If something is named well is an orthogonal concern, and highly contingent on what the actual object is. The best code in the world might be impossible to name intuitively if there is no real world analog.

Google has posted warning signs to look out for in code (1). One of them is very similar:

  Suspicious names: context, environment, principal, container, or manager
(all of the things google mentions, are things I've seen much, much more in Java codebase than anywhere else)

(1) http://googletesting.blogspot.com/2008/11/guide-to-writing-t...


This is the full article mentioned above.

166 violations, of the 4025 classes/interfaces in java 7:

That's 4% of Sun's own JDK, not counting user defined classes. In my 15 years of Java pgmming, I've seen enterprise programmers almost always start out their OO design with a class named "xyzmanager"...probably reflecting their subliminal desire to become a manager someday :)

------ AbstractDocument.AttributeContext AbstractRegionPainter.PaintContext AbstractRegionPainter.PaintContext.CacheMode AccessControlContext AccessibleContext AppletContext BAD_CONTEXT BeanContext BeanContextChild BeanContextChildComponentProxy BeanContextChildSupport BeanContextContainerProxy BeanContextEvent BeanContextMembershipEvent BeanContextMembershipListener BeanContextProxy BeanContextServiceAvailableEvent BeanContextServiceProvider BeanContextServiceProviderBeanInfo BeanContextServiceRevokedEvent BeanContextServiceRevokedListener BeanContextServices BeanContextServicesListener BeanContextServicesSupport BeanContextServicesSupport.BCSSServiceProvider BeanContextSupport BeanContextSupport.BCSIterator CompositeContext Context Context ContextList ContextNotEmptyException ContextualRenderedImageFactory DirContext DOMCryptoContext DOMSignContext DOMValidateContext DragSourceContext DropTargetContext EndpointContext EventContext EventDirContext FontRenderContext GSSContext HttpContext InitialContext InitialContextFactory InitialContextFactoryBuilder InitialDirContext InitialLdapContext InputContext InputMethodContext JAXBContext LdapContext LogicalMessageContext LoginContext MathContext MessageContext MessageContext.Scope NamespaceContext NamingContext NamingContextExt NamingContextExtHelper NamingContextExtHolder NamingContextExtOperations NamingContextExtPOA NamingContextHelper NamingContextHolder NamingContextOperations NamingContextPOA NoContext NoContextHelper NoInitialContextException NotContextException PaintContext RenderContext ScriptContext ServiceContext ServiceContextHelper ServiceContextHolder ServiceContextListHelper ServiceContextListHolder SimpleScriptContext SOAPMessageContext SSLContext SSLContextSpi SSLSessionContext StyleContext SynthContext WebServiceContext XMLCryptoContext XMLSignContext XMLValidateContext _NamingContextExtStub _NamingContextImplBase _NamingContextStub ActivationGroupDesc.CommandEnvironment Environment GraphicsEnvironment GroupPrincipal JMXPrincipal KerberosPrincipal Principal Principal PrincipalHolder ProcessingEnvironment RoundEnvironment UserPrincipal UserPrincipalLookupService UserPrincipalNotFoundException X500Principal AdapterManagerIdHelper BeanContextContainerProxy CertPathTrustManagerParameters Container ContainerAdapter ContainerEvent ContainerListener ContainerOrderFocusTraversalPolicy CookieManager DefaultDesktopManager DefaultFocusManager DefaultKeyboardFocusManager DesktopManager DirectoryManager DomainManager DomainManagerOperations DriverManager ErrorManager FocusManager ForwardingJavaFileManager GSSManager JavaFileManager JavaFileManager.Location JComboBox.KeySelectionManager KeyboardFocusManager KeyManager KeyManagerFactory KeyManagerFactorySpi LayoutManager LayoutManager2 LogManager ManageReferralControl ManagerFactoryParameters MemoryManagerMXBean MenuContainer MenuSelectionManager NamingManager POAManager POAManagerOperations PropertyEditorManager RepaintManager RMISecurityManager RootPaneContainer ScriptEngineManager SecurityManager ServantManager ServantManagerOperations StandardJavaFileManager ToolTipManager TrustManager TrustManagerFactory TrustManagerFactorySpi UIManager UIManager.LookAndFeelInfo UndoManager ---------

Relevant snarky blog post from Steve Yegge:


I particularly dislike classes called *Helper

I'm with you there. Aren't all classes supposed to be helpful?

I am upvoting this because I think it is an essay worth thinking about, but I’m not sure if I agree with it.

I was lucky enough to get an equivalent piece of advice from "Object Oriented Programming" by Peter Coad & Jill Nicola ( http://amzn.com/013032616X ) which was the main textbook for an OO course during my CS studies. There are good examples there that illustrate this and many other useful rules (of thumb) of OO modeling.

The most correct program (in terms of maintainability and evolution -- as well as mathematical correctness) approximates the most orthogonal program.

In term of modeling it with objects that contain state, this may mean having objects that have a varying degree of state (unless you choose other purer approaches).

But it's naive to think that all classes should be a noun. Everything should be an abstraction that models the solution. However, nouns that end with 'er' do tend to have a little bit of state that seem unorthogonal. But I guess my point is that that's the wrong way to look at it. Better to think about it in terms of DRY. Some 'manager' or 'controller' classes violate DRY (whereas it's rare for 'connection' classes to). But not all 'manager' or 'controller' classes do.

Even better: don't use objects at all. Use a functional programming language.

Object oriented programming is the sweet spot solution to many problems. For example, if you have a UI with several text fields, it's natural to think of them as objects that have methods like setText(), setEnabled() and such. These methods often have side effects and that's their whole point.

Of course functional programming also has use cases for which it is the sweet spot solution, e.g. writing compilers.

These methods often have side effects and that's their whole point.

You don't need state and mutations, GUIs can also be programmed nicely using functional reactive programming (FRP). A nice description of FRP:


Some examples of FRP in GUIs:


A simple counter that can be manipulated with two buttons "Up" or "Down": https://github.com/HeinrichApfelmus/reactive-banana/blob/mas...

    main = start $ do
        f       <- frame [text := "Counter"]
        bup     <- button f [text := "Up"]
        bdown   <- button f [text := "Down"]
        output  <- staticText f []
        set f [layout := margin 10 $
                column 5 [widget bup, widget bdown, widget output]]
        network <- compile $ do
            eup   <- event0 bup   command
            edown <- event0 bdown command
                counter :: Discrete Int
                counter = accumD 0 $ ((+1) <$ eup) `union` (subtract 1 <$ edown)
            sink output [text :== show <$> counter]
        actuate network
This doesn't look like good UI code to me. It seems to use too many advanced FP concepts that have nothing to do with the task at hand. Are you sure this is better than the OO approach? Why?

This doesn't look like good UI code to me.

It replaces a mutable counter (that could potentially be changed anywhere in the program) by a definition of the counter that describes all possible changes it can have during its lifetime:

    counter = accumD 0 $ ((+1) <$ eup) `union` (subtract 1 <$ edown)
So, we have a counter that has an initial value of one. Over time, 1 can be added and 1 can be subtracted, namely when repsectively an eup or edown event occurs.

Being able to capture all possible 'state mutations' of a value in one assignment is good.

It seems to use too many advanced FP concepts that have nothing to do with the task at hand.

Which is a bit ironic, since the widgets are created in the IO monad with the 'do'-notation, which simulates imperative programming in Haskell :). A user interface should be designed with a WYSIWYG interface (Glade, Qt Designer) anyway, but that's besides the point.

> Being able to capture all possible 'state mutations' of a value in one assignment is good.

I think there's a duality here. If there are 20 buttons, each of which can affect some of 30 counters, then the Haskell code would say "this counter is affected by these buttons", while imperative/OO code would say "this button affects these counters". It's not obvious to me that the former way of organizing code is better.

You are still missing the point. In imperative programming there is no guarantee that the counter isn't modified in some place else or wrongly. In the end it's just an integer that allows all kinds of mutations.

The possible value changes in the Haskell FRP example, on the other hand, are completely described. There is no other manner in which the value can change.

OOP also has a way to guarantee that the counter can only be changed by calls from specific parts of the code, it's called encapsulation. For example, the counter could be an object that subscribes to button events, and the counter's value could be a private member variable of that object. What exactly is the win from FP here?

I don't know Haskell but it appears this code is just setting up a few GUI elements and defining two events and their effects.

What does good UI code look like?

Aside from, possibly, the monadic notation—which is just that, a notation, an artefact of the implementation rather than of the idea—what are the advanced FP concepts here?

I'm overreacting mainly because these last days I saw code with some Java classes just acting as data holders. It's verbose and obscures the intend.

Data deserves much more than being encapsulated in objects, data deserves the right to be put in front of the programmer so that he better sees how ill-designed his data model is :-)

Well, he briefly touches on that: As long as you haven't joined in the Functional Monks in their Monasteries of Statelessness, programs are made of behavior and data.

I thought this was the best part of the essay (at least it made me smile).

I see, your bullet is apparently more silver than mine? :)

Of course, it's a bullet designed with maths :-)

Functional programming and objects are not orthogonal as you suggest.

Yes, they pretty opposite.

Objects should have identity and can be distinguished by identity alone. They are not referentially transparent because of that. In "x = new O(); f(x,x)" you cannot substitute "new O()" into both occurrences of x.

Functional values do not have identity and you cannot tell apart 2 and 1+1. Functional values are referentially transparent. In "x = 1; y = x+x" you can change x to 1 in the definition of y.

Again, they're not opposite. You can create most of the properties of an object with a closure and versa vice.


I really respect the man and his work, but I can't help groaning whenever I read the "I invented the term 'Object Oriented Programming'…" Alan Kay quote. Always struck me as a bit puerile and pedantic.

Well, this is one of my reasons for python over Java as a programming language. There are some procedures that don't fit naturally into a class or object and I should be allowed write functions for them. I was a OOP fan, when i learnt c++, but when i learnt java was put-off by its' requirement of needing a class. I am learning Haskell now, and am a little infatuated with FP, but not sure will enjoy using it a couple of years down the line. I understand the Manager class has some state and methods, but just can't digest the idea of a separate class. To overextend the metaphor, i guess, that's one of the reasons i have shied away in my career from becoming a manager.:-P

Just curious, why downvote. is anecdotal evidence frowned upon here? Or only opinionated, argumentative positions welcome??

good explanation of procedural vs OO programming, but I got kind of lost with the -er tip, is it all about naming conventions, should I go and rename all my ZzzXxxer classes to XxxxZzz?

yes, that's his advice. Organizer -> Organization, don't ask, just do it. while this sounds silly, i think it's all about getting into the right state of mind.

Lots of Smalltalk lately in HackerNews :)

But I just met 'er!

This is interesting in view of Go's convention to use the ending -er for interface types (Reader, Writer, ...) rather than the implementations of those interfaces.

Haha. Look at the Ruby home page: http://www.ruby-lang.org/en/


Registration is open for Startup School 2019. Classes start July 22nd.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact