
The Wrong Kind of Paranoia - akerl_
http://prog21.dadgum.com/206.html
======
AdeptusAquinas
In my view the purpose of const/protected/internal/static etc is not so much
to prevent mistakes by myself or others, but to embed in my code a 'living
documentation', enforced by the compiler.

If I mark a method as internal, that means I intend it for reuse within the
library but don't expect it to be used by any external caller, which means
another developer can come along and make changes to it without needing to
worry about anything outside the library (short of those in C# using
[InternalsVisibleTo] or similar, which hopefully is restricted heavily to
obvious test projects).

By decorating my code appropriately, I don't need to write comments most of
the time, which means I don't run the risk or filling my code with out-of-date
or poorly understood annotations that become useless almost as soon as I
finish writing them.

~~~
rdtsc
I agree with this. The code is not just a message to the compiler to build the
binary/bytecode, it is also a message to other programmers. As such there is
value in annotating things in that manner.

Even in Python, a very dynamic language, there is a convention that methods
that start with _ are private. And I often also separate them into a different
(bottom) section with a big header saying this is the private block of
methods. So in that case there isn't a compiler rule but rather a common
convention (which is kind of a step above comments if you wish).

~~~
rpedroso
If you want to enforce this more stringently, you can even prefix methods with
__ (double underscore). Python will mangle the name:

    
    
        __mydef -> _myClass__mydef
    

You can, of course, still access the method, but it's very useful for keeping
implementation details of a base class out of a subclass. This way, your
subclass can have its own `mydef` without overriding the base class.

------
haberman
These protections might be overly fussy in small, one or two person projects.
But for programming in the large they are essential as they represent a social
contract between teams.

If you call only my public methods, you can generally expect:

    
    
       - if my public methods don't work properly, it's a bug
       - and I, as the library author, promise to care
       - if the internals of my library change, I'll keep my public
         functions working if possible.
    

Disregard my protections and all bets are off. If you cast away const on an
object I gave you and then call a non-const method, you might be totally
violating the threading model of my library. If you write and complain about
this, I will ask why you thought it was ok to cast away const.

Without these annotations it would be much harder to effectively communicate
and enforce the parameters within which the library is "promised" to work.

------
Swizec
Over here in the dynamic languages world (JS, Ruby, Python, etc) I often cry
myself to sleep because I spend hours debugging something that turns out to be
preventable with a const or a private function or something.

~~~
TheLoneWolfling
Python being Python, you can prevent non-malicious problems of the sort you
mention (const and private functions / variables). Const via overwriting
setattr / etc, private functions via stack inspection (among other things.
There's a sliding scale of thoroughness versus clean code here.)

Though it won't protect you from malicious intent (though this is still the
case with C / etc), and it's caught at runtime as opposed to at compile time.

~~~
Swizec
> Though it won't protect you from malicious intent

It often does feel like junior developers are malicious. While I commend their
ingenuity in solving the problem, code review can be a real schlep.

~~~
TheLoneWolfling
Hanlon's razor

------
kstenerud
Static also allows the compiler to make better optimizations, knowing that
nothing outside of the current compilation unit will ever use it.

Const also allows the compiler to optimize better with the knowledge that the
called function won't perform any writes to the object state.

Access modifiers give you a way to separate out functions that can be called
externally vs those that shouldn't. The API doc generator won't know which is
which unless you mark these correctly. You use access modifiers to show
intended use and keep things clean, not to lock someone out. Same goes for
internal classes.

~~~
plorkyeran
In practice const very rarely allows any interesting compiler optimizations
(passing a reference to a const local variable to a function whose body cannot
be seen is pretty much the only place where it's safe to do so). The const
overload of a function with const and non-const versions could in theory be
faster, but copy-on-write data structures are the only common example of that
actually being the case.

------
bucky
I completely agree we should stop being paranoid about what's going to happen
when we let other people loose on our beautiful code.

But I don't think having private methods or marking variables const is about
paranoia, it's about communicating intent to the people who come behind you
and using the compiler to enforce that intent. Every piece of extra context
you can give to someone reading your code helps them understand why it's
there. Identifiers like const and private are almost like nonverbal
communication for code.

------
opinali
I usually love James Hague's posts, but this one has his head buried deep in
the sand. And he's enough of a veteran to remember early Lisp and Smalltalk
systems, where excessive openness in all aspects looked awesome for
prototyping/hacking or academic toy programs (or what is considered toy-size
in the last 20 years anyway), but was a disaster even for the mid-1990's
definition of programming in the large. Attempts to discipline these languages
came too late, so they got overrun by competitors with lots of "paranoid"
rules like C++, Java, hell even Eiffel. (Not the single reason of course but
one important reason; not even the gurus could help themselves to not abuse
dark magic and write application code that would be compatible across minor
updates of the language/frameworks, let alone other compilers/VMs/platforms.)
If you need extra evidence with more current technology, see most "bad parts"
of Javascript (successful, but in the same way that the Titanic was wildly
successful for one week; people who build large JS apps have been in a mad
rush to fix or replace it for the best part of the last decade, hopefully with
ES6 coming as a first major win in that direction).

TLDR sorry James, we have tried the elegance of extremely simple and open
language/runtime architectures, and that was always an abject failure.

~~~
jack9
> we have tried the elegance of extremely simple and open language/runtime
> architectures, and that was always an abject failure.

Just because it was tried in the past, doesn't mean it wasn't the right
direction.

> Attempts to discipline these languages came too late,

So, as an industry, we were learning and now we know some basic principles.
Discipline does a great deal for Erlang. Ironically the game sockets he
describes are basically how Erlang functions at scale. To say the practice is
always an abject failure is burying your head in the sand.

~~~
opinali
You didn't understand what I meant by "discipline". I didn't mean best-
practices -- i.e. programmers learning to not abuse openness without any help
from the language. What I meant was adding the missing controls to languages
(making them more similar to the languages James criticizes) so proper
discipline could be enforced: visibility, module systems, restrictions to
change core runtime classes, optional static typing etc.

------
underbluewaters
This post resonates with my experiences of the last 6 months. I'm a very good
web developer, and have been working for a while now on a couple iOS projects.
Coming from javascript which everyone seems to regard as dangerous and overall
terrible, I expected that after some time I would get used to and appreciate
working in Objective-C and Swift. Nothing could be further from the truth.
While you can shoot yourself in the foot with javascript, in practice it's not
really an issue. The absurd lengths that these languages (particularly Swift)
go to make you less productive while preventing errors is ridiculous and
unhelpful. I get as many runtime errors writing Swift code as I do javascript,
but my code is riddled with as? or foo.bar!.method() cruft just to satisfy the
compiler and get on with my life. Rather than thinking about how to solve a
problem I'm strategizing how to work within the limitations of type systems.

As someone who learned to code with javascript, ruby, and python, I'm not sure
I'll ever really appreciate these "nanny languages".

~~~
buttchrist
This old canard.

I am a professional Objective-C developer and have been for several years.
Type safety is not an absurd length. Eliminating an entire class of errors
from your program by having the compiler infer and enforce types is not
ridiculous. Using a type-safe language is a very good idea.

We are human. We have stupid unchecked nil object errors come up in our code
bases all the time. Swift will ensure that does not happen again. That's like
the least part of what I am looking forward to.

If you are having trouble writing a program that compiles with strong typing,
I don't know what to tell you. Using types is nothing more than stating what
you expect the shape of the data to be in and having the compiler make sure
that is so.

~~~
vinceguidry
Is it really worth that much though? I'm a Ruby developer and I've only very
rarely actually wanted a type system. I've often found that I could work
around its lack by implementing class checking and raising an error whenever
the wrong type gets passed. It gave me everything I wanted without having to
give up dynamicism.

I think types probably work well in situations where you don't know what kind
of code you're going to have to deal with in the future. It depends on the
type of organization you're in, not the type of problem you're trying to
solve.

I think any respectable programmer should try to avoid having to have people
hook into his code at any level other than the level he defines. To interface
at the level of data, not client code. If you are needing typing to solve your
own inadequacies as a programmer, you should become a better programmer rather
than expect your language to do that for you.

With a dynamic language, you can get all you could have wanted from a type
system without having to infect your whole codebase with it. Most of the time,
you just don't need it.

~~~
actsasbuffoon
I agree that some type systems are infuriating. They demand highly verbose
code, and offer almost no protection in exchange for your effort.

However, not all type systems are equal. Haskell (for example) almost never
requires explicit type annotations. It has a type inference system that is
sometimes frighteningly good. You can express a huge amount of logic through
the type system and enforce very non-trivial constraints.

I've been writing Ruby for 7 years, and I've loved every moment of it. It's a
wonderful language. That said, I usually have to spend quite a bit of time
getting my code to work correctly. In Haskell, by the time I get to the point
where the type checker approves of my code, it usually works as I intended the
very first time I run it. It's a wonderful feeling.

~~~
vinceguidry
The problem isn't the type annotations. The problem is losing the meta-
programming capabilities you gain when you can make everything an object with
a class. These capabilities are really useful when you don't really know what
you're doing yet, which, for me, is almost all the time.

Knowing at any time I can take the class hierarchy I just built, turn each
class into an instance of an object, and store those objects in a database,
with a 10 minutes and a fancy bit of code, is much much more useful than
having a babysitter.

------
Mithaldu
"There's an architecture used in video games for a long time now where
rendering and other engine-level functions are decoupled from the game logic,
and the two communicate via a local socket."

Is he talking about multiplayer or has anyone ever actually seen this?

~~~
catmanjan
Some games (even in singleplayer) host a "local server" and environment
information is passed to that. If you're making a networked game it makes
sense as you'd be creating the server anyway, decoupling is nice and less
codebase to manage.

Steam's Source games, UT do this, if you open up the console and scroll up you
can see the local server initializing.

~~~
douche
For Source, that's probably the GoldSrc/Quake legacy. You can see this
somewhat in the source on GitHub - WinQuake has both client and server mashed
into one process, but there are clear divisions, with the cl_ _.c /h and
sv__.c/h files.

------
simula67
I wonder how much the software state of the art is advanced by software
developers as opposed to software maintainers.

When you are working to refactor software written by others, things like
"private methods" are a gift. If you are trying to refactor some piece of
code, you only have to make sure that the callers within the said class are
modified to guarantee that the codebase is not broken.

I don't think a lot of people understand that maintainers in the real world do
not have the time to read and understand every line of your code. And the
toughest part of doing software maintenance is figuring out how much you can
safely ignore. I feel qualifiers like "private" were designed to help with
this problem.

I also severely dislike the architecture proposed by the author with loosely
coupled services talking over a socket. This style of code is only
maintainable if you understand the entire system inside and out. For example,
lets say the maintainer receives a ticket that says 'Report X has wrong data'.
She will start investigation with the question : 'Why does it have wrong data
?'. She will walk back up the call tree looking for why and eventually learn
that the data coming off the socket is wrong and that is where the trail ends
(unless there is a document describing who is responsible for putting said
data there).

I have faced this issue in real life. I can understand when this style of
decoupling is necessary to improve modularity, but it does not have a positive
maintenance impact.

~~~
buttchrist
Apple have added a fantastic thing to iOS 8 and OS X 10.10 that I am terribly
interested in but have managed to find nothing about beyond this post[1]. I
think you'd be interested, it addresses exactly the problem you describe by
including information from other threads and _processes_ to crash logs.

[1] [http://www.objc.io/issue-19/activity-
tracing.html](http://www.objc.io/issue-19/activity-tracing.html)

------
plorkyeran
> If they're not in the tutorial, examples, or reference, you don't even know
> they exist. If you use the header file for documentation, and internal
> methods are grouped together beneath the terse comment "internal methods,"
> then why are you calling them?

Autocomplete is a thing. Even if there is a big comment explicitly saying not
to use a function, if it doesn't show up in the autocomplete window someone
will inevitable use the function anyway (and sometimes even if it does).

~~~
mikestew
If I'm reading this correctly, you're saying that someone will use a function
for which there is no documentation, no external reference, and the user
hasn't even seen the code (since they didn't see the comment saying not to use
it)? Just a function name and (maybe) a method signature in the autocomplete?
The caller deserves whatever woes befall them.

~~~
douche
This kind of thing provides endless material for Raymond Chen's blog[1]. It
also explains a lot of the insane backwards compatability that is still in
Windows.

[1][http://blogs.msdn.com/b/oldnewthing/](http://blogs.msdn.com/b/oldnewthing/)

------
j42
To be clear, static typing is the zenith of of compiler optimizations and in
many ways the programming "ideal," however in the context of loosely typed
languages (and their OOP abstractions) and I think the author is missing a
significant use-case.

Separation of responsibility/decoupling on the service level are good
principles to begin with, but in an OOP paradigm where you are
defining/exposing classes and methods, these keywords are really useful for
grouping functionality when you adhere to "contract-driven development."

When used correctly they help you abstract the interface for your class (ie,
the public implementation-agnostic methods that provide interoperability
between the service and the program as a whole) and separate it from methods
only designed for internal use (within the class itself).

Often times those abstractions are essential (DRY principles) and the class is
the proper place to encapsulate them, however you wish to clearly designate
that "this method is self-modifying, limited in scope, and does not interact
with or is required by any other class in any meaningful way," and for that
it's quite useful.

~~~
chipsy
A nice standard reply. But how do you verify that your usage has the impact
you think it does?

When I started estimating the complexity of my code - just using rule of thumb
and line counts - I found that most uses of classes were unjustified. The
"right size" of a class was quite large, especially so in the top level of an
application.

~~~
j42
Well, I can't speak necessarily for the way you write or view code, but for my
process I've found its impact intrinsic and clearly definable.

First I should note that I follow a few basic principles/sets of principles
when programming loosely-typed imperative languages.

1) Single-responsibility for classes and methods, with each method coming in
around <= 20 lines. In total the majority of my classes in enterprise level
applications (supporting 100's of millions of users & responding to internal
events with meta/statistical analysis) rarely grow beyond 200 lines per class.
Usually whenever I hit the 2-500 line range I'll find that many methods can be
logically abstracted to a few core traits in order to take advantage of
multiple-inheritances without overcomplicating the central registrar or DI
patterns.

2) I build an interface before building a class--the interface defines what
methods will be available to the application/world context (thinking as per an
API interface), as well as what type(s) should be received and what type(s)
should be returned. By type hinting/checking at this stage and ensuring
conformance to an interface, you can swap out implementations easily later, as
well as have a general map or "spec" before you really start hammering the
nails--this helps in staying organize and weeding out bad architecture
decisions early.

3) I keep these public methods simple, so that I can clearly detect failure
points and debug based on input/return types for the 'service' as a whole, and
then I use _protected_ methods internally similarly to data pipes in
functional programming; each method is clearly named, has a specific
transformation it applies, and acts on a series of (n) objects by mapping
transformations vs iterative loops which precludes un-terminated conditionals
and other type-juggling weirdness.

This all results in software that's very concise (IMHO), runs well, and is
quite simple to test. I can inherit any class from a testclass, in order to
test the protected data pipes with any sample streams. I can verify the I/O
types for all interfaced methods of the class (via functional tests), and tie
it all together neatly in knowing that I can pull up any file and clearly
differentiate between what formats/returns/transports data, and what mutates
it and/or the "state."

The process is by no means perfect and I continue to learn in my pursuits as
do we all, however this structure has worked well for me consistently on the
types of large projects where others have failed, and in that context I feel
it's worth expounding.

------
Sophistifunk
Some of these things are simply to "nanny" us, but there's a very good reason
for private / protected: The Namespace is a precious and limited resource.
It's a good-thing(tm) not to pollute it with internal details, and what is
unexposed need not be well-named, which is after all one of the 2 hard
problems (along with cache invalidation and fencepost errors).

------
r00tbeer
Most of the commenters here didn't seem to read James' whole article. He's not
saying that you should throw away isolation but that isolation should be taken
care of from your architecture, not your programming language. Of all the
places that should be up on a microservices architecture and see the sanity of
it, you would think that would be here on HN, no?

------
blt
`private` is a joke in C++. Coupling happens at the header file level, not the
class level. Any C++ programmer who cares about reducing dependencies should
be thinking about header files, not classes.

Man, we really need a module system.

I think the problem is not that `private` exists, but that it is taught/sold
to programmers as a silver bullet for making software architectures better.
People think "Decoupled systems are good. `private` hides my variables from
the outside world. Therefore, if I use `private` variables, my system will be
decoupled." This is false. A program can use `private` extensively and still
be tightly coupled.

So, I agree with the author that `private` encourages myopic engineering, but
I think education is a better way to fix the problem than removing the
features.

~~~
quanticle
>Man, we really need a module system.

And you might be getting one:
[https://isocpp.org/files/papers/n4214.pdf](https://isocpp.org/files/papers/n4214.pdf)

It's under consideration for C++17, as I recall.

EDIT: Also, CLANG allows you to use modules right now, but IIRC those are
CLANG specific extensions and the final module spec. may or may not be
compatible with them.

------
sreejithr
I find the assertion that compiler enforced discipline is paranoia, too hippy.

What it does is free you from a big overhead, which is keeping track of the
access levels for all of your instance variables. Moreover, documenting
enforcements is a very shaky way to go about it. It requires military
discipline. I've seen time and time again, documentation which didn't get
changed with the code.

------
anabis
I actually want _more_ possible restrictions such as limiting rage of values,
or allowing only printable characters in a string.

As other comments say, its a documentation to future self and others.

