Better Java – Resources for Writing Modern Java
github.com
github.com
While I agree with most things, overuse of Optional like this is an antipattern IMO. Having done a lot of Java 8 development, I find Optional is best for return types, but otherwise forcing callers to wrap values in Optional is unnecessary as opposed to something like Scala. Those that work on the JDK feel the same [1][2] and I think [2] is even overuse with the prevalence of static analysis tools and @Nullable.
Although a tad dated, I find [3] to be a good guide to modern Java all around.
1 - http://stackoverflow.com/questions/26327957/should-java-8-ge... 2 - http://blog.joda.org/2015/08/java-se-8-optional-pragmatic-ap... 3 - http://blog.paralleluniverse.co/2014/05/01/modern-java/
Here are some more thorough treatments: http://stackoverflow.com/a/23464794 http://blog.jhades.org/java-8-how-to-use-optional/
C#'s prior behavior gives me hope for languages I'm currently paid to care about like Java and Swift on this issue.
After trying dropwizard for a few projects, the superiority of the spring-boot and its various ecosystems just blew me away.
Also, while I appreciated Gradle, Maven is just much more supported through the entire eco-system. As a new comer to Java from C++/Python, it made a big difference.
As to Maven vs. Gradle, that turned out to be the most controversial point of the entire guide -- which surprised me. Let me put it this way: both are good and I prefer Gradle. Also, Gradle, while still far behind Maven in terms of adoption, has good momentum. Either is a good choice at this moment, but I would guess that Gradle will take the lead in five years (although it's far from a safe bet).
Could you give some examples? I haven't had much problem with Gradle support, and Gradle's essential advantages over Maven fat outweigh what little I have had.
Then I think of it and I see the real problem is languages making all nonprimitive types into pointers without being explicit. In a language like C it's more obvious what can be null.
IMHO Optional just feels like another typical Java dogmatic overabstractionism. "Null pointer exception? So what? Just catch it." Then again, I mostly use C/Asm.
I can see how a C/Asm programmer wouldn't like it though. Very different mindsets. :)
sigh
IMO the problem with Optionals is that they'll never suffice unless they are part of the language itself, like in Swift. As this article pointed out, Optionals aren't supported in any existing APIs, so you still have to do a ton of null-checking everywhere. And there is nothing that guarantees an Optional reference itself can't be null, which is why you really want language-level support.
For example, Haskell has Maybe which is not part of the language nor treated specially by the compiler in any way. It is part of the Prelude (think "standard library"), so that (basically) all libraries can agree on what "Maybe a" means.
error: (stringp nil): not a string
I think it indicates Lisps suffer from this problem too...I think the best compromise is to declare a new data type for the argument, such as:
data ExecutionDirectory = ExecuteInCWD | ExecuteInDir FilePath
And take an ExecutionDirectory instead of a `Maybe FilePath`.Of course, you need light-weight data declarations for that.
The problem with the struct approach is that numerous tooling / libraries expects beans with typical getter/setters - Jackson Json, Spring binding, etc. Yes it's possible with configurations and/or annotations to get around this, but IMO, saving a few lines of code in exchange for non-default behavior isn't a valid trade-off.
Adding a dependency on Lombock is even worse.
Honestly, I can spot a Bean class within 1 second of opening it up. Unlike Scala or other languages, Java programmers typically use one class per file, and beans rarely get in the way as they're typically contained in some 'model' package anyway.
Sometimes you want a computed property and you use non-trivial getter. Sometimes you want to do some work in setter. And then client's code will look terrible: point.x * point.x + point.getY() * point.getY(). There are other problems: binary incompatibility; non-trivial refactoring when I need to introduce a getter or setter.
Java really need lightweight property syntax which would solve all those problems. I don't understand why it isn't introduced yet.
[1]
public class MyImmutable {
public final String field1;
public final int field2;
public MyImmutable(String field1, int field2) {
// assign
}
// equals, hashcode, toString
}
[2] https://github.com/jqno/equalsverifierThat said, I agree that java _really_ needs something like this built in.
Then pre-compute it, make it immutable, and expose the variable. And if that ends up being too hard it's probably because you need a builder and/or to group your properties into smaller classes.
Not sure what in particular you're referring to, but Spring supports constructor based dependency injection, and I tend to insist upon it vehemently - getter/setter injection means that you can instantiate your object in an invalid state.
It's also kind of a nitpick, but I disagree with his preference for Guava's Maps.newHashMap() vs new HashMap<>(). I'm pretty sure the only reason those static methods exist is because pre-Java 7 didn't have the diamond operator.
Other libraries like Immutables are safer for this purpose.
And Guava's collection creation methods are part of an intense desire to not use the new key word anywhere in application code.
http://docs.guava-libraries.googlecode.com/git/javadoc/com/g...
The collection creation methods existed because Java could perform type inference on methods but could not on constructors:
// Java 1.5
Map<String,Integer> map = new HashMap<String,Integer>();
// Guava
Map<String,Integer> map = Maps.newHashMap();
// Java 1.8 diamond notation
Map<String,Integer> map = new HashMap<>();Basically they refuse to use version control (FTP their code to the live server), use a terrible dev system, non-existent or terrible staging environments or newer technologies such as composer or other tools which will save them time and effort.
Don't even get me started on the lack of tests, and how buggy and broken their code ends up being - "we don't have time to write tests", yet spend weeks fixing critical issues that would have been found...
/rant
Java also lacks the ability to say something like:
class Foo<T> implements ISerializable iff T implements ISerializable
So you end up having to work only with T's that are serializable, or not having the instance for Foo.Also, Java lacks type-classes.
Java also lacks lazy bindings, and many other useful features one might need.
In short, Java is still very far from having everything one might need...
I'm a C and C++ guy, but I've done a tiny bit of Java a while ago.
Such counting is analagous to counter-based looping. If you'd said "I can probably filter down to the fingers of one hand..." I might have believed you.
You must have worked at some sweatshops to spout such biased vitriol.
Sadly you missed the part where I said, "Not all Java devs obviously". I have read great blogs by Twitter engineering and many others.
But, since you seem to have some experience in sweatshops, please explain how your current situation is different. Add something to this conversation instead of correcting my stupid generalizations.
Edit* Yep, just checked your comment history, you're a troll. God why do people like you exist.....
This is what brings on personal attacks. The attitude in which you post. If you fail to see my original point, ask for clarification. It would have caused me to edit my original and change my tone.
Take a look at the top comment in the thread for a) an actual substantive criticism of the article b) a link to a much, much better survey article on the same topic.
SparkJava (mentioned) is the great hope for sane java web development:
Having to convert out to streams for data structure manipulation with lambdas is insane. It would have been very healthy for the core java team to pull in someone with a lot of ruby experience so they could get that API right.
Too bad: the JVM and associated tools are fantastic.
I don't see why. It's explicit and allows you to chain higher order methods without building up a ton of intermediate data structures and convert to any data structure you like very efficiently.
It's still not efficient as imperative approaches but it's close enough and far better than most collection libraries.
If you are unhappy with candy-shop utils class and are ready to split them into smaller entities, go the whole way there. Create proper small classes, and inject them using your chose DI technology.
Anyway, kudos to the author, that was a risky article to put together and there was no way to write it without controversial recommendations. That's a good start for what a new java developer need to look for.
Also, I don't think that's what default methods are for. The new StreamUtil libraries uses default interfaces. It's not migrating anyone, but the default methods describe intrinsic properties about the classes that inherit the interface. A predicate can be in sequence of another predicate, for example.
I don't quite understand your point about instance variables. Instance variables are and should be encapsulated. An object that implements multiple interfaces must of course provide the correct semantics for all of them. But that's true whether an interface contains method implementations or not.
For one - some performance examples (specifically, integer array iteration) is 15 times slower with streams.
Angelika Langer's analysis:
https://jaxenter.com/java-performance-tutorial-how-fast-are-...
That's a very misleading example, and even the linked post itself explains why it is so (e.g. not much is done with the integers in the first place).
Array: 453 ms Stream: 887 ms Parallel stream: 120 ms
The array loop code is about twice as fast as the sequential stream (parallel stream blows both of them away). The array loop advantage will start to go away once you increase the complexities of what you're doing in the loop and, of course, the stream code is much more readable.
(Edit: I also tested IntStream().of() instead Array().stream() for the sequential stream test, the results were identical in both cases.)
If not for the main application code, I would definitely use Groovy for unit testing in combination with Spock Framework. You just have to try it once to realise what you've been missing all these years with JUnit.
On a related note, I really encourage Java teams to shoot for a goal of having compiles that finish with no warnings. If there are generic warnings littering your code (like, for example, all the warnings about unqualified references to generic types that you get when using code written before generics existed), bite the bullet and either fix them, or make it explicit that you don't care and add the appropriate annotations to turn them off. At least that way, when a new, interesting warning shows up, maybe people will pay attention to it.
And yes, this is something of a "do as I say, not as I do" thing, as I have definitely written Java code that doesn't follow this rule. Especially on my personal projects or code where I'm not working with a big team. On bigger projects, I do try to push that as much as I can, depending on my role on the project.
These two statements seem to be in opposition to each other.
(All your advice is spot on. If you let warnings remain in code, you'll just end up accumulating more and more warnings until it's too much to fix. I guess it's a sort of "broken windows" effect.)
So yeah, those tools aren't perfect, but my subjective experience has been that they help, as long as you tweak the knobs so that they don't just generate something equivalent to the "broken windows" compiler warnings, etc.
EDIT: False positives tend to indicate that there's something wrong with your analysis.
I suppose this is a general thing: If a warning system alerts you every five minutes, what are you going to do? Are you going to react and go on full-adrenaline-rush every five minutes, or are you just going to turn it off after two or three false alarms?
EDIT: Sorry, OP = you. Edited correspondingly.
Spring has had code-based configurations since 2009[1]; seems a little disingenuous to recommend Guice/Dagger due to this non-existent limitation.
[1] http://spring.io/blog/2009/12/22/configuration-simplificatio...
Spring with a very strict, carefully enforced style guide is probably as good as Dagger or Guice. But there is a cost to maintaining and enforcing a style guide. Better to just use something that doesn't have the bad options.
Everything is autoconfigured and set to sensible defaults. All the dependencies in the starters are levelled and guaranteed to work together.
Spring Boot on Gradle is the closest thing Java has to adding lines to a Gemfile in a Ruby system. Especially when you add other Spring automagical libraries (Spring Data REST, Spring Cloud...).
> It [the Spring framework] has a either code-based wiring or XML configuration-based wiring.
I attend quite a few university hackathons as a mentor/sponsor and one of my goals has been to paint Java in a better light as it doesn't have the best reputation in that circuit. I'm always telling people that their goal should be to build something cool and learn something new.
It's much easier to teach someone how to build their first webapp in a weekend when you can do it in a language that they are familiar with and Java seems to be what's taught at most universities.
The Spark Framework mentioned here is very well done and beginner friendly. I recently wrote an intro blog post [0] about it as an attempt to embrace existing knowledge and showcase that it's easy to build something cool with Java.
0: https://www.twilio.com/blog/2015/09/getting-started-with-gra...
Also I'm not a fan of how in your example the entire app routing/logic is basically crammed into a single main method. I know it might be simple demo app that you're trying to show off Twilo but I don't think you're doing favor by structuring like that.
What do you feel are the benefits of Spark vs Spring Boot (or Dropwizard I guess). In my mind the only reason I wouldn't use Spring-Boot for a new project in Java is if I wanted to use something like Play! since supposedly using Play!/Scala and futures integrate very nicely into that.
The biggest is DI. Having witnessed what a tangled mess large Guice codebases can become where you really have no idea what is providing what I have to say that Spring here gets a bad rap from the all-XML days and (IMHO) it is actually the cleanest and easiest to work with.
The author mischaracterizes Spring too:
> It has a either code-based wiring or XML configuration-based wiring.
Actually Spring can use a mix of XML code configuration and auto-wiring.
A modern Spring application will have precisely an application context XML file with one line per "bean" eg:
<bean class="com.foo.SomeClass" />
and that's it. Note: there's no classpath scanning. Auto-wiring takes care of the rest. This has two big advantages (over Guice in particular):1. Everything is in one place. If it's not instantiated here it doesn't exist. Guice can be many levels deep and tends to descend into ModuleBuilders and conditional logic in modules (which the docs recommend against but tends to be used extensively anyway); and
2. Testing. Testing is incredibly easy. I'm talking functional testing here, even integration testing. You simply include another XML file that overrides any definitions from the first. Everything else remains the same.
Testing with Guice can be horrendous and once you start making extensive use of Modules.override() you should abandon all hope.
I'm glad to see this post recommend Maven. It's fun to bash Maven but most detractors ignore the huge benefit that applies to any shop with lots of devs: Maven is opinionated. That's a plus not a minus. Seen one Maven Web application and you've seen them all. There's no really weird directory layout because someone decided they just had to be different.
Lastly Play. I really tried to like Play. I really did. But as of Play 2.0+ it became a Scala not a Java framework. It may still work with Java but some things just require you to know something about Scala. That's a huge negative IMHO. I'm not interested in Scala. Nobody is.
Plus the auto-compile feature of Play tended to randomly break with indecipherable error messages for no readily apparent reasons such that you couldn't tell if it was a bug or not. I get enough of that with C++ template programming thank you.
I also agree that the two books the author recommends are probably the two most important Java books in existence.
Their use of Scala was the only reason I gave the play framework a second look. Just because you're not interested in Scala doesn't mean nobody else is. Scala is the most popular plugin for intellij by a very wide margin, that's a lot of 'nobody' downloads. Having used both java and Scala extensively I honestly can't understand why anyone would still pick java by choice other than if it's the only jvm language they know.
But that one place is XML. Better to have it be code, where I can use all my normal tooling ("find references" in my IDE (yes some IDEs have spring support), grepping through .java files, automated refactoring...) to work with it.
> Guice can be many levels deep and tends to descend into ModuleBuilders and conditional logic in modules (which the docs recommend against but tends to be used extensively anyway)
Have you seen the kind of horrors that are possible in Spring XML with conditionals, proxying, interceptors...? Compare like with like.
And in the worst case, if you must put conditional logic in your config, at least if it's in code then you can unit-test it the normal way.
> Testing. Testing is incredibly easy. I'm talking functional testing here, even integration testing. You simply include another XML file that overrides any definitions from the first. Everything else remains the same. Testing with Guice can be horrendous and once you start making extensive use of Modules.override() you should abandon all hope.
Shrug. Not my experience; you get more-or-less equivalent tooling capability with both (that is, you can group your definitions into smaller modules, and you can override specific instances when testing), so if you use them the same way it'll be just as easy or hard.
I prefer my Spring applications to look like this:
@SpringBootApplication
public class Application { ... }Getters/setters is a pattern that it took me a while to reject, having always been told that public fields were really, really bad. But they're simply not. Having logic in getters/setters is the only justification for this added bloat. In my experience, in the overwhelming majority of cases (never say all) having logic in getters and setters is a clear indication that your design is flawed anyway, so most of the time you should never have them.
On this part:
> Further, this class is immutable unless you extend it, so we can reason about it easier as we know that it can't be changed.
Well, I'd go further. Make the class final if you really want to guard against people extending it. Of course this is most applicable for writing reusable libraries where communication with consumers may be limited.
If you rely on language features to enforce development practices in a team who can freely communicate, in general you've got a much bigger problems than the few rare bugs that extending an immutable class and making it mutable will cause.
Obviously though, putting guard rails in can only ever be helpful, even when absolutely everyone understands that there's a cliff edge there, and wouldn't conciously try to walk over it anyway.
Obviously, this can apply to many sorts of fluent API. This is at least part of what Tony Morris is alluding to in his essay on API design [1].
[1] http://blog.tmorris.net/posts/understanding-practical-api-de...
the builder should fail with a good error message if there are required fields you didn't supply to the builder.
Another groovy-ism.. the whole deal with having to copy properties. (In a groovy class they're threated like a map)
Except in hard to remember special cases like .class which gives the class "LinkedHapMap" of the map object instead of the value at key "class". After working with Groovy for a short while you'll realize you're really working around it.
Unrelated to builders, the stream example could be simplified further with method references
final List<String> filtered = list.stream()
.filter(s -> s.startsWith("s"))
.map(String::toUpperCase)
.collect(Collectors.toList());
It would be nice if Oracle back filled some predicates to make them more useful in streams (e.g. "s"::isPrefixOf, etc.).http://benjiweber.co.uk/blog/2014/11/02/builder-pattern-with...
Regarding your isPrefixOf - is there a significant benefit to that being in the standard library? You could define a isPrefixedWith("s") that returns a Predicate<String> yourself.
You can achieve this through constructors or setters; Builder itself is agnostic.
The only reason to use streams, i think is the possibility to automatically parrelize, and secondarily to make it more readable.
The Java compiler will unroll the methods anyways, so you get easy readability without any loss of performance.
Two main ones: Mockito is, IMHO, much nicer than jMock. And Immutability is good, but 'final' is... complicated. I find it's often over used by people who don't know why they're using it.
A lot of people write "final" in front of every. single. declaration. And they do that because someone once told them that was good. But then you remember that Java is a language which all non-primitive types are passed by reference, so your 'final' is actually a final reference and not a const immutable object, like you might get in C.
If someone passes in a final FooBar to a method, and you mutate the FooBar during the method for any reason, then after the method is done the object remains mutated.
Just this week, that exact problem caused my team an entire day of headaches. Two methods were being called concurrently using Futures, passing the same object to each method. One method mutated the object slightly, the other relied on it having not been mutated. Hello race condition. I was so proud to have solved it, until I realized I'm the dolt who wrote the bug in the first place.
Silly, I know. But the point is that final makes you feel safe when you might not be.
Fair enough, but if FooBar is your class there's an easy fix for this - make FooBar immutable. If it's not, make an immutable wrapper object for it.
Very true that relying on untrue assumptions about the immutability of your data will cause hard-to-spot bugs, but the solution is then just to guarantee everything is immutable, and define that as an assumption that cannot be false if everyone on the team follows the pattern.
If somebody does break the pattern, then that, rather than the race condition down the line, is the real bug. The dev who wrote the mutable data class can just have that (re-)explained to them, so it doesn't happen again.
Honestly I don't think final ever makes things worse. I struggle to imagine someone who would misunderstand in the way you describe having any success in Java - references are a pretty fundamental part of the language.
You had better mock everything, and better put them in the exact order they will occur in, or your test fails.
Most JMock unit tests I see are essentially the exact same code as is being tested, written a second time in the unit test. So if I want to refactor or modify this code in any way, my unit tests break- even if functionally, my tests are doing exactly what their spec says.
With Mockito, I can say 'Hey, if anyone calls this method with parameters sort of like this, give them this response', and then focus on my unit tests actually testing the output for a given input.
Use constructors. This makes perfectly testable classes and is vastly simpler than encouraging more XML as code. Any proper dependency injection framework should work quite well with regular constructors.
The main advantage of keeping the constructors around is that you retain more flexibility in interacting with that bit of code.
public class MyClass {
@Inject private String foo;
}
versus public class MyClass {
private String foo;
@Inject public MyClass(String foo) {
this.foo = foo;
}
}I'd also say that making circular dependencies not possible is a feature, not a bug. They go against everything we've learned about computing in the last 20 years. Entire languages do not have variables, or at least heavily discourage them, and the gains outweigh the costs.
Setter dependencies have other major disadvantages, like how easy they make creating partially initialized objects: Refactor something in one place, adding a parameter, and good luck making sure that the change is made everywhere else. A constructor guarantees fully initialized objects, and that's a great things.
Java pays for the mistakes of the early 2000s: The Java Bean pattern, and all the infrastructure around it, makes Java code be full of default constructors that force us into mutable objects and nullable fields.
If there is a defense for your argument for setter dependencies, is that we cannot count on any of the nice things that come with, say a Scala case class, because so much Java code out there is built with standards from a less enlightened era that using what would be better standards in any more modern language leads to inconsistencies, due to all the ancient code using Spring, Hibernate, Guava and EJB code that is hanging out there, and that would lead to inconsistencies for those trying to make Java actually use some of its new functional programming inspired features.
If you have more than 2-3 dependencies, it might be time to refactor things, as your class may be doing too many things.
> Second: circular dependency is not possible.
Circular dependencies are possible with constructor injection when using a DI framework (e.g., using a Provider in Guice [1], or the equivalent in Dagger).
Regarding the second: I think circular dependencies should be avoided whenever possible so I view the difficulty constructors create there as advantage rather than a problem.
https://en.wikipedia.org/wiki/Circular_dependency#Problems_o...
Perhaps things being too big to wire together manually is a smell that the application is getting rather large.
There are also other patterns that can be used to make manual dependency injection less of a pain. I wrote about some of them here
http://benjiweber.co.uk/blog/2014/09/13/frameworkless-depend...
I think asserting that Fluent interfaces are 'more readable' or better is largely a matter of opinion.
final List<String> filtered = list.stream()
.filter(s -> s.startsWith("s"))
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
>Instead of this: final List<String> filtered = new ArrayList<>();
for (String str : list) {
if (str.startsWith("s") {
filtered.add(str.toUpperCase());
}
}
>This allows you to write more fluent code, which is more readable.I don't agree. The second snipped is far more readable. This seems like an effort to use lambdas just to be using lambdas.
Not only that, but the non-lambda version is far easier to maintain. When you get exceptions in code that has the form
this().that().theOther()
it's generally not obvious where the problem is.I have been coding Java (and C-style) languages for a really long time. And, I still have some cognitive delay when traversing multiple indented scopes. Not a long delay, but long enough to be slightly annoying.
With the lambda style, I can quickly glance down the text column to see what's going on. Stream -> Map -> Filter -> Collect.
After using lambdas enough, one starts to subconsciously cull the stream() and collect() calls. So, when I look at it, I really see Map -> Filter.
But what really, really annoys me is that exception handling can't be applied to the whole stream processing statement. This leads to code that looks like:
collection
.stream()
.map(o -> {
try {
return o.oToC();
} catch(Exception e) {
// handle
}
return new C();
})
.reduce((c1, c2) -> {
try {
return c1.combine(c2);
} catch(Exception e) {
// handle
}
return c1;
});
For that, I have had to write a bunch of helper functions that wrap and throw the exceptions.Addendum: I’m also of the opinion that trying to write java code without an IDE is highly inefficient. With an IDE, you can locate all the throws you’re not taking care of quickly, and usually quickly deal with them to get things to compile. Yeah, it’s a bit of housekeeping overhead, but once they are dealt with, they are dealt with, even if it is just passing them upstream.
One thing I'm surprised he didn't mention is exception handling. Namely the growing acceptance that checked exceptions are dumb. I make a point that except for a few rare circumstanes, my code should never have a throws clause. I always created subclasses of RuntimeException (or Spring's NestedRuntimeException) and use them exclusively. Any libs that throw checked exceptions get either handled or rethrown with a custom RuntimeException.
Final by default is the biggest, bc it removes some of the most basic concurrency errors implicitly. Awesome post. It also is pointing out things that in some other languages (e.g. Python) are implicitly impossible to achieve, and others which some new languages (e.g. Rust) have built in from the beginning, like never use null.
Well done.
Is it still Spring ? Are you guys doing things like Socket.io , etc.
And is Hibernate the preferred orm ? Confession: I'm an ex Java programmer, who have been working in rails and python for past several years...so am fairly out of date. I have kept up with things like Dropwizard...but have not seen many other startups do Java development.
Spring 5 will take Java 8 as the baseline.
The problem with writing Java without an IDE isn't one of tooling, it's that the language is so very verbose. They're working on that with lambdas and generics inference, but there's a limit to what you can do and remain backwards compatible.
It uses JavaScriptCore and starts practically instantly. There's some work being done to port it to Linux/Windows as well.
Here's an example:
cat foo.cljs
#!/usr/local/bin/planck
(println "hello world: clojurescript -> planck -> as shell script")
./foo.cljs
hello world: clojurescript -> planck -> as shell script
Planck can be installed using homebrew now as well.
Another promising option is Pixie https://github.com/pixie-lang/pixie
log4j: now log4j2
jOOQ => $, JPA, MyBatis
I do agree that it's less important, but it's so easy to do that there is no excuse for not doing it or giving out such rubbish advice. Anyone that wastes a day on formatting is doing it wrong. The problem with advice like this is that people take it to the extreme and ignore formatting altogether. That leads to someone being frustrated and formatting all the files automatically (as should be done anyway) which then leads to possible conflict and crappy diffs. Instead, you could have had professional programmers work on these. In an industry that's touting how code readability is so important (and it is), this advice is toxic. We're finally getting people to realize that readability is the most important part for most code because it is. Honestly, formatting is important in this inconsequential reply, in essays, in everything we write except short chat messages. Imsurenoonewouldliketoreadsentenceslikethis just like no one wants to read obfuscated, unformatted, crappy code. So yeah, I disagree strongly because formatting is important, get one style, get proper tools to automate it, and then you don't have to spend a day putting in spaces (which is ridiculous).