Something everyone has noticed over the past couple years is how Amazon has pushed its way into retail space. And most notably, the publishing industry. Listening to a new podcast (the Sell More Books Show), it appears Barnes & Noble may be on their last legs. They are implementing cost cutting measures that reduce staffing. These type of cuts are the things companies do when they have no idea how to innovate and generate interest. It appears to be a similar thing that has led to Toys’R’Us planning to close ALL THE STORES. These are NOT the moves of someone ready to beat Amazon and actually make money.
So exactly what COULD B&N, or any other company trying to sell books do to rival Amazon? Surprise answer: it’s not hard. But it takes effort.
The secret sauce is in Amazon’s royalty rates. If you dig into their tables, you’ll find that anything between $2.99 and $9.99 earns you a 70% royalty. Above or below, you are knocked back to only 35%. Amazon does this for a reason – consumer training. They want customers to get used to paying only $2.99-$9.99 for an e-book.
So what is this advantage we have access to? Amazon’s 30% cut off the top. Someone like B&N could offer to pay authors 90% instead of 70%.
Imagine if the CEO of Barnes & Noble started doing something useful, like phoning the top 100 big time authors with this deal.
CEO: “Mr. Patterson, we’ve got a deal for you. If you come over here, we’re willing to give you 90% of the take on e-book sales as long as you talk it up through your channels.”
James Patterson: “90%? My own publisher has been talking for months with Amazon trying to get special dispensation to move off that 70% rate, and Amazon won’t budge. I’m in!”
Rinse. Repeat. The next 99 calls could turn the corner on the popularity of B&N.
Another effort would be to reach out over the most popular indie channels. Call up the head of the ACFW. Reach out to the Top 50 Romance indie authors of 2017. In fact, sic the whole board to find the Top 1000 indie authors and call each of them.
I say board members because the authenticity of B&N’s top management reaching out would really move the needle. You can say “Wow! Pretty expensive to have some board member calling individual authors, ehh?” Sure, but it would increase the number of people willing to take such a leap of faith.
And a leap of faith is what B&N may need. Otherwise, they’re dead. I give them 12-24 months. Are they going to do it? I doubt it. But some outfit is going to have to figure out this move. Otherwise Amazon will blast EVERYONE. Except maybe Walmart. Still watching to see how their partnership with Kobo unfolds.
NOTE: This idea isn’t wholly my own. Much of this was inspired by Jim Kukral on the Sell More Books Show.
I have worked on this novel for eight years, which is why I’m excited to report that Darklight is released!
Could a pickpocket be the source of freedom in the Earth’s distant future?
Snitch, a young woman who grew up on the streets of Kelmar as a thief, has discovered something terrible. The regime’s evil military ruler has learned the hideout location of the resistance she joined and ordered an all out assault. Combined with the captain of the disbanded royal guard, a political prisoner, a duke’s daughter, and an old advisor, can her team survive and free Kelmar?
Snitch moved as fast as she could in the dark toward the southern edge of the city-state of Kelmar, desperate to not draw attention to herself. Fortunately, rain had caused a haze to fall.
He was coming. Snitch couldn’t believe it. She had to get this information back to the Undergrounders if they were to have any chance to escape.
Her knowledge of this part of the city told her to keep a sharp lookout for rival gangs; gangs that had risen to fill the power vacuum left behind when Melicose purged Kelmar’s palace of all of its nobility.
As she exited an alley, a gust of wind made her clutch the edge of her coat and pull it tight. Steeling herself, she pressed on.
Melicose was coming, according to one of her contacts. What were they going to do?
She approached a familiar corner and slowed when she recognized members of the Raiders. Glancing back, she gulped. Too late to find another route.
“Snitch,” Marlon boomed. “What are you doing here?”
He was the Raiders’ second lieutenant or something. She couldn’t keep track of the ever-changing titles.
“I didn’t know you guys had moved into this block.” Her lips pressed flat as she shifted her weight between her feet. “I was trying to stay off your turf.”
Tall and dark, he crept closer, eyeing her. The others drifted in behind him. “You haven’t answered my question.”
Snitch knew Marlon wouldn’t take kindly to any sort of brush off. She’d heard enough stories of people crossing street gangs, and the last thing she wanted was to become another story.
To read more, click on the link below:
Are you a writer? Have you have written novels? Tech books? Do you want to? Assuming you said yes to any of those questions, the next question is possibly the most critical one to follow: Are you building your email list?
If you said “no”, then you might be making the biggest mistake of your writing career.
In this day and age, we are heavily encouraged to grow our followers on Twitter, Facebook, Instagram, whatever. I suffer from the same affliction. As soon as my follower count crosses the next 100-mark (recently crossed 1600), I cheer for about five minutes, and then start asking “are we getting to the next one?” (1700 in my case).
But studies have shown, over and over, that social media is NOT the most effective mechanism to make contact with your readers. Social media followers don’t complete sales very well. I’m not saying that if you have 100,000 followers, you can’t sell a book. But the conversion rates are quite dreadful.
- According to a 2016 article, the average conversion rate for social media traffic is 0.71%. That means out of 1000 followers, you can only average 7 sales.
- The same article shows that email conversion rates are 3.19%.
Basically, if you had 10,000 followers on Twitter, don’t expect more than 70 to buy your next book. However, if you had 10,000 subscribers to a newsletter, you could look at 319 to buy your next book.
And there is another factor buried in all this: who exactly owns your following? When it comes to Twitter and Facebook, the platform not you owns this list of people. You aren’t granted a spreadsheet of everyone that has liked your Author page on Facebook. If you were, you could email them directly. Instead, you can post something to your group, but only a subset will receive the message. That is, unless you pay FB a few bucks.
Reports now show that Facebook only reaches 2%-4% on average (unless you cough up some $$$). People that started their businesses out years ago purely on Facebook are feeling the pain of watching their accumulated audiences get taken away.
The flip side is that an email list, something that takes time to curate, is a collection of people more likely to be interested in what you’ve written. That is, if you curated it correctly. Something I was introduced to a couple years ago are reader magnets. The concept is simple.
- If you want to sell books to people, the goal is to find people that like your work. Otherwise, writing a book would be like performing for an empty room.
- To find these people, you need to give them something of value in exchange for their email address. Like a short story, listed for free on Amazon.
- Inside that free book, you have a big ad at the beginning and the ending that takes them to your website.
- On your website, you have a big banner that says “Sign up and get ANOTHER free e-book”.
- They sign up. You give them something else. Perhaps another short story. Or deleted scenes that were dropped during the edit of your novel.
If you offer people something of value, people will join your email list. With that in place, you have the means to constantly grow your email list. From there, anytime you release a new book, you include links to further build your email list. Everything you release focuses on building your email list. Bookmarks, flyers, slideshows, anything. Wherever you speak, whether its with other people at a conference or on stage speaking, you make it easy for people to give you their email address.
The law of big numbers says with a list of 10,000 emails, on Day 1 of a book launch, a 1% conversion rate would turn into 100 sales. And that isn’t the end of it. Book sales aren’t based on selling purely to your email list. In the land of publishing, there is this thing called “reviews”. The more you have, the more legitimacy you sport.
Imagine selecting a small subset of your email list. Say…9%. That would be 900 people. Six weeks ahead of book release, you send each of them a FREE advanced copy of your novel, requesting, in exchange, an honest review on Day 1. Assume that just 1/3 of those 900 people decide to download it. And of those 300 people assume only 1/3 actually follow through and post a review. That is 100 reviews on Day 1. The technical term for such an accomplishment is a slammus dunkus. Scoring 100 reviews will push your novel up the charts and drive sales far beyond what you could sell just to your email list.
I must confess something. This isn’t 100% my idea. I learned the secret of reader magnets from Nick Stephenson. His freebie book above is of keen value, and I have also learned much by subscribing to his newsletter. The thing is, if you don’t build your audience through an email list, you will find the publishing process a sad story. If you want to be an author, you’re not out to trick people into buying your book. You simply want to find your audience. And the best means to doing that is through an email list that you own.
So, happy writing!
A big event is coming. The release of my debut novel, DARKLIGHT, is planned for later this month. And do you know what I dread? A lackluster launch. I have helped launch several indie titles in the past for other authors, but something I finally knuckled down and did this morning was what EVERY author must do: keyword analysis.
When you publish a book on Amazon, there is one section toward the bottom. Past the title, the author, and the description of your book is a section called “keywords” with seven blank spots. Until the past year, I never paid much attention to those. I thought it was a little extra stuff to help out. What I didn’t know is how important this tiny bit of information is. This could be the difference between getting ranked as the 50,000th Science Fiction title to be released this month. Or showing up in the top 100 of Teens & Young Adult / Coming of Age novel.
So what IS a keyword? Basically, it’s what a user types into either Amazon or Google’s search box. (HINT: A keyword isn’t confined to a single word.) Imagine a reader being interested in something about “coming of age fiction” so they type that, and a list of books are shown. By putting “coming of age fiction” into ONE of your keyword slots, you are adding your book to that listing, and upping your chances of a sale.
Keyword analysis is when you try LOTS of keywords in Amazon’s search box and gather notes, answering these questions:
- Are many people searching for this keyword? (No good using a keyword no one types.)
- Are there too many titles that respond to this keyword? (No good picking something so generic that you instantly have 100,000 competitors.)
- Do the titles under this keyword actually make money? (I think you get the idea here.)
Those three questions answer the critical question “Is this a good keyword?” Additionally, is this keyword good enough, or is there a similar one that would be better?
Try to imagine going to Amazon and typing combo after combo and trying to gather these details. Sounds hard if not impossible, ehh?
That’s why you need the right application. An application that do these searches for you and give you instant feedback.
Introducing KDP Rocket.
KDP Rocket is an app created by Dave Chesson, an author that likes to brag at averaging over $9000/month in book revenue. And watching his videos about KDP Rocket shows the power of having the right tools at your fingertips.
With KDP Rocket, you kick things off by starting with an “idea”, i.e. your first keyword. Now I had learned last year that Amazon has these “super secret” genres that require picking one genre + a special keyword. Since my title is Young Adult, I had learned that there are parallel genres that focus on that subset of readers.
Having already heard that it’s best to dominate a niche rather than languish in a broad genre, I dug in to discover WHICH YA genre DARKLIGHT would best fit. Seeing “Coming of Age” and “Action & Adventure”, I focused on that. Typing “coming of age” into KDP Rocket, I discovered that there are 693 searches for that keyword every month on Amazon. Clicking on the “Analyze” button I saw a competition rating of 99. That means that it’s VERY competitive, and probably not a good keyword for a first launch.
So trying some different options, I uncovered “coming of age fantasy fiction”. That keyword revealed 884 searches/month on Amazon with a competition rating of 68. That means MORE people are searching for this keyword, and (drum roll) there are fewer titles flagged with that keyword. BOOM! I wrote that one down. This keyword would potentially rank well and also put my title into the YA Coming of Age genre.
- coming of age fantasy fiction (competitor rank 68)
- coming of age adventure (competitor rank 45)
- coming of age book (competitor rank 35)
- science fantasy action and adventure (competitor rank 80)
- medieval future earth (competitor rank 25)
- medieval coming of age (competitor rank 25)
- medieval science fantasy (competitor rank 45)
So for my coming of age, sciency fantasy, action & adventure story set in the Earth’s distant future, in the midst of a medieval existence, I feel that I’ve found seven valuable keywords to help my story reach the readers that would most connect with it and hence are most likely to buy it.
And that’s what keyword analysis is all about. Connecting your novel with readers. Using KDP Rocket, I was able to research probably 50 keyword combinations in the span of one hour. Imagine doing this all by your lonesome.
So I’m crossing my fingers hoping this will feed my book launch that’s right around the corner.
By the way, if you sign up for my newsletter below, you can get a FREE E-BOOK prequel to DARKLIGHT:
Today is super exciting because I finally get to reveal the cover to my debut novel, DARKLIGHT.
I’ve been working extra hard on this work. I started it in 2010 and have been at it for eight years. It took three editors and four rounds, but it’s finally gotten that extra polish it needed.
At the same time, I’ve been working with AMDesign Studios on the cover. We went through several rounds of tweaking and adjusting, and I can’t help but be excited about it.
Snitch, a small young woman who grew up on the streets of Kelmar as a thief, has learned something terrible. The regime’s evil military ruler has learned the hideout location of the resistance she joined and ordered an all out assault. Combined with the captain of the disbanded royal guard, a political prisoner, a duke’s daughter, and an old advisor, can her team survive and free Kelmar?
It’s not too late to sign up for my newsletter and get a FREE PREQUEL:
I used to chant the line about how static methods in Java were evil. In fact, you can find funny articles and stack Overflow discussions about how this is the case. So why am I shouting from the rooftops that statics are no longer evil?
To answer that question, it’s important to realize that everything in a given language is a tool. A means to an end. The reason features take on such negative vibes is when they are used for evil. Like sharing state between objects.
When a class has a static variable, it means that ALL instances have access to this one “thing”. This mechanism was used to bootstrap things like the Singleton Pattern and the Flyweight Pattern, both things that involved a shared state between all instances that came with nasty side effects and an aversions to testing.
Singleton instances showed up in public facing APIs (even some Java standard APIs) and hence became very hard to evolve as well as test. With 20/20 hindsight, it’s crystal clear how the world moved away from these mechanisms and toward Dependency Injection. These static instances were VERY resistant to extension, testing, and ensuring a consistent state, given they had a completely different lifecycle inside the JVM compared to “newed” objects.
Hence, static were dubbed EVIL. And this led to a Butlerian Jihad against all statics.
But the landscape has changed. That previous stuff was in the era of Java 1.2 through 1.4, a time we have almost put out of our memories thanks to the leap forward of Java 5. Today we stand on the mountain of Java 8, surrounded by streams, method references, functional interfaces, and IDEs ready to transform any SAM into a lambda.
The ability to use a side-effect free function like Math.max() inside a lambda by method reference illustrates a purity we haven’t seen since Java’s inception. The ability to write small, tight functions, test them extensively, and then inject them with no risk of side effects from the surrounding object is a feature not a bug.
Let me repeat: static methods with no side effects from the surrounding objects are quite useful.
Another feature now available are static imports, making it simpler to compose functions and use them in functional flows. Being able to chain functions together without collecting in between state has let us move away from imperative programming and toward functional, reactive programming.
Static objects holding persistent state are a huge risk. They persist state between unit tests unless deliberate steps are taken to setup things up properly. In a day and age where we are trying to run either 10 or 10,000 copies of our app simultaneously, static state is NOT Cloud Native. But side-effect free static functions are.
None of this was possible with Java 1.4. That’s why statics had very little upside back in the day. So the judgment it received was most justified. If you saw “static” in someone’s code, they were probably doing something VERY WRONG. (Albeit at the direction of some of the most popular books back then). I cast that keyword into the pit myself. But today is different and to repeat the same arguments in light of Java 8 is short sighted and pedantic.
Statics are a important and we should embrace them instead of burying them under a rock.
So go forth and use statics. But do it correctly. Think: small, composable, testable functions.
If all this talk about functional flows, side-effect free programming, and method references is whetting your appetite, be sure to go grab your copy of Learning Spring Boot 2.0 2nd Edition. The entire book is written in the functional paradigm thanks to Project Reactor, and built on top of the Spring portfolio’s state of the art code base.
Some of you may remember that in a former life, I launched a podcast with friend and former teammate, Russ Miles. Suffice it to say, that was shelved long ago. However, everything I learned came rushing back when my wife announced last year that she was auditioning with her very own publisher to record the audio version of her novels. Thus launched a new project. Although by the time I got involved in this project, she had already learned a ton of things from the Turntable Guide about all in one record players and turntables. Now that I am a member of this project first thing, we will need for a recording studio.
You’ll need a microphone
The very first thing you’ll need is a microphone. Here is where it really pays off if you’re father-in-law has a degree in Music Ministry and has spent years recording music in his own studio. We asked him for details on a good mic, and he gave us one of his spares. In place of that, I’d suggest going out and doing a little shopping. You can easily spend $10,000 on what’s considered a “studio grade mic”. In this day and age, prices have come down and it’s easier to shop around online. Just don’t assume that something costing $9.99 isn’t going to sound equally cheap.
Cost: $50-$100 will get you something decent
You’ll need a shock mount
Ever watch a radio show streamed as video? You’ll notice that each and every microphone is wrapped in this squirrely contract. Tip: it’s called a “shock mount” and stops vibrations from getting picked up by the microphone.
You’ll need a mic stand/boom
That shock mount needs to be attached to something. You either need to get a mic stand (we have a couple since Sara and I played in a church band for a year) or you’ll need a boom that connects to your desk. Which one you prefer really depends on your configuration. If all you plan on doing is recording books, podcasts, or videos, a boom may work wonders.
You’ll need a mixer
This is what will power your mic. While you can hook a passive mic (like the one that came with your iPhone) into your laptop, any serious mic needs power and laptops aren’t built for that. Good news–the simplest mixers cost about $40, and if you can splurge up to $100, you can get do all right. However, if you want a mixer to handle lots of inputs and can be controlled from your iPad, then you’ll have to spend closer to $250.
You’ll need a desk
When it comes time to record, you need somewhere to put your laptop, the mixer, and other things. Root around the house and see if you don’t have a tiny desk that you can repurpose. If not, visit either Ikea, Office Depot, or another similar store. No need for a fancy desk. A small place to put your stuff will do.
Cost: $40 and up
You’ll need Audacity
The software to sink your time (but thankfully, not your money) is Audacity. This open source application lets you edit audio, mix audio, clean things up, just about anything you can imagine. Learning to use it is of extreme value.
I once recorded a podcast episode and moved some questions around due to a logistical boo boo on my end. You can also edit out tiny “pops” and more. Learning to drive this app is a useful skill.
You’ll need a studio
Okay, for those of you that have seen a REAL studio, those places are incredible. They have scientifically developed wedge foam lining the walls. Sophisticated electronics. And they charge anywhere from $100-$100/minute to record material.
Since you’re not doing THAT, here is what you need: a room where there IS NO ECHO.
This may not be as clear as you realize. My wife recorded the opening chapter of her debut novel in her office, with no one else. When I listened to it, it was terrible. Not due to her performance, but the audio was “tinny”. After much scrubbing and filtering, I couldn’t improve it, deducing that the desk/walls/pinball machines/etc. had caused sound to bounce around and degrade the sound.
I took her mic and mixer board to the only room with wall-to-wall sound absorbing material…her closet filled with clothes, and did a test recording. AMAZING. No bouncing sound. No echo. No tinny sound. (The image is NOT Sara’s closet)
So I dug up a tiny desk she never used, found an extension cord, and set up all the things listed above in her closet. Following that, Sara found some opportunities where all the kids were asleep or all at school (no good recording when the 4-year-old is running around upstairs!)
So, assuming you scoop up all these things, the minimum price for all is (drum roll):
And you don’t have to do every step all at once. Ease into it. But know if that you want to record audio, this is the way to go. In this day and age, you really are in power to create content if that is your heart’s desire.
I’ve spent the last several weeks picking up something I started back in 2015. Way back then, in the airport departing SpringOne, I started working on the third mediatype for Spring HATEOAS. There were already two: the original one based on pure Jackson and HAL.
This was Collection+JSON by Mike Amundsen. As a traveling consultant, speaker, and author, Mike has actually penned several mediatypes while also advocating for evolable, adaptable RESTful architectures. Working on this one, I made considerable progress. The progress was really about me learning how Jackson worked.
When your pull request gets turned down
You see, Spring HATEOAS doesn’t just crank out hypermedia. We have a collection of domain classes that represent a neutral format. Your Spring MVC controllers can actually be used to generate multiple forms of hypermedia. At least, that’s the idea, and in it’s formative six years of development, that premise hasn’t been dropped.
So after supposedly finishing the job and submitting it to the project lead, Oliver Gierke, I wondered why my work wasn’t getting merged. I chatted with him and learned of another effort afoot that was blocking its acceptance: Affordances. Another group of people were working on a very deep, extensive enhancement to Spring HATEOAS, and Oliver didn’t want to complicate things by having yet another mediatype in the baseline.
Two years later, when I rejoined the Spring Data team, I picked up the mantle of Spring HATEOAS. Oliver’s responsibilities had grown including become the official manager for the Spring Data umbrella. One of my biggest undertakings was to start reading through this Affordances API handiwork and bring it forward. Doing so required months of reading, testing, experimenting, polishing, and coding.
When your pull request gets turned down…again!
My months of effort were clobbered by the inalienable fact that the code had suffered major feature creep. It had also been forked into a couple other branches. Despite months of massaging a pull request that had over 200 commits and getting all of its tests to pass, Oliver and I agreed to shelve that work in favor of starting over.
Yes, I proposed starting over and trying to implement the same concept from scratch. If you want a hint of how long this took, you can see it in the commit logs. Because we squash pull requests, this causes the timestamps to go back to the original, first commit. This image shows code merged on November 29th, yet the commit for the Affordances work dates July 13th.
Ultimately, I crafted a much lighter weight API that allows chaining multiple Spring MVC methods together. With that, Spring HATEOAS can unpack these related links into the right format for each mediatype. For example, HAL-FORMS looks at any POST or PUT methods, finds the input type, and extracts its properties.
Testing an API by using it with another mediatype
So my big test with Collection+JSON was to take this two-year-old effort, rebase it against all of Spring HATEOAS’s improvements, and see if a completely different layout of JSON could be derived from the same vendor neutral representations INCLUDING this new Affordances API.
Step one was to rebase this entire chunk of code. Since it had previously been written against Java 6 and Spring HATEOAS was now on Java 8, that alone was a bit of effort. Several contracts had been altered meaning not all the implementations worked. Had to hammer that out.
Then in the midst of revamping things and reading the spec in detail, I noticed something unfortunate: I had completely misread the format of data. I thought turning a POJO into a simple JSON object was okay:
It looked like Collection+JSON’s “data” field would let any type of nested domain object get turned into a simple JSON structure.
Turns out, Collection+JSON requires a very explicit array of key-value pairs:
This is not nestable unless done out-of-band.
Additionally, it required that I code up the ability to extract property names and values from objects for serialization, and go the other way for deserialization. This is kind of like serializing inside the serializer. Yech! But I got it done. So after several weeks, I managed to rewrite half if not more of my original code and get things working.
I had to hook into the Affordances API and generate custom things pursuant to this new mediatype. Two years ago, I was very aware of Collection+JSON’s “template” and “queries” sections. And I hadn’t a clue how to populate them. Until now.
This would be the moment of truth at how well I had designed this API for HAL-FORMS. Turns out, the API was quite well suited to handling this.
Coding the Affordances API, Oliver had recommended creating an AffordanceModel and AffordanceModelFactory to allow mediatype-specifics to be neatly encapsulated behind a incredibly simple interface until the mediatype’s serialization code could unpack it. Turns out that was a very good decision.
Debugging the code, I could see that a model was now being generated for both Collection+JSON as well as HAL-FORMS, and I only need one of these for any given situation. Having this complex, detailed code is most important, and handily doesn’t leak into unnecessary areas.
I had allowed some Spring MVC specifics to leak into my HAL-FORMS handlers. That was a no-go, because the whole idea is to have things be tech neutral at this point.
One of my last tests was to have one Spring MVC controller wired up, and have it serve queries for two different mediatypes.
With this in place, I began to engage in classic jürgenization. This has taken me several days. I discovered a missing pair of units (serialize/deserialize custom extension of ResourceSupport) and hence missed a critical corner case. Crisis averted, I have pushed off the final set of commits to Travis CI and submitted it to Oliver for review.
Using one mediatype to develop another
Having built HAL-FORMS, I enjoyed leveraging all the lessons learned to build up Collection+JSON. It gave me a starting point for tests that needed to be written, a nice repeatable pattern of serializers and deserializers that also had to be tackled.
And having just recently performed the work for HAL-FORMS, I was ecstatic that when I solved a particular problem for Collection+JSON, I was able to refactor HAL-FORMS to use the same solution, ensuring consistency between the two.
Every time I work on another mediatype, the whole project gets stronger, better, more reliable. That’s why I can’t wait to circle and resume my efforts with UBER Hypermedia. Most of the work is done. Or at least, that’s how it appeared when I was last there!
But I wanted to let it rest, and circle back. WIth three mediatypes implemented (HAL, HAL-FORMS, and Collection+JSON), UBER will be a real sizzler. After that, SIREN, and then possibly XHTML.
See you then!
I’m a pretty big test advocate. After all, it’s in my profile. So how can I say that TDD is wrong?
“Test-bitten script junky…” — opening of my profile
The “test-bitten” means I’ve been bitten by the automated testing bug. In a previous post, I mentioned having built the equivalent of a CI solution early in my career without knowing it. So how can I advocate such a heretical point of view?
The answer is subtle. To me, the benefit of automated testing is in having the testing and automating it.
- Nowhere do I see primary benefit in writing the test first.
- Nowhere do I see it better to write the test then write the code that solves the test.
Transitioning into my current role on the Spring team has moved me into the land of framework design. Building frameworks is quite different than end-user apps. That’s because the code you write is meant to serve not one but many. And in the case of Spring, we’re talking millions of developers (no exaggeration).
When serving this many people, you are building APIs, implementations of APIs, and ensuring that all kinds of scenarios don’t break that API. So I often have to start writing the API first. I try to create some fields. Add the accessors I need. Try to chain stuff together. And then I begin to write test cases that poke at it.
Several Spring projects also use Project Lombok. This is a really neat toolkit that I’ve known about for years, but only in the past two years have I truly come to appreciate it’s power. It makes it possible to stop writing getter/setters/equals/hashCode functions, customize visibility of accessors, define data classes, value classes, builders, and other stuff. All with a handful of easy-to-read annotations.
Trying to write a test case and then writing a Lombok-based class is ineffective. I’d rather create the class first and then use it’s pre-packaged structure in the unit test. Using Lombok in this way ensures a lot of very consistent structure that makes the overall API easier to consume. For example, it’s Builder annotation produces an API that looks like this:
This example is the builder I defined for the hypermedia format of Collection+JSON. It lets you lay out all the parts of a CollectionJson record, which is then serialized into JSON. The class behind it looks like this:
This class has several things, so let’s take it apart.
- @Data and @Value create setters and getters, along with equals, hashCode and toString methods. This is the core for a Java object, but not the bits directly needed for a builder.
- @Builder creates the fluent builder shown earlier with collectionJson the static function to create a new instance of this class.
- @JsonCreator is simply used to connect Jackson to this class when it comes to deserialization.
- And because the Item class is also a builder, I have item() as a convenience method to dive into this “sub” builder.
That’s it! This class is highly coherent because there is little “code” as in logical stuff being done. Instead, it’s mostly declarative. This class isn’t buried in logic, because it’s focused on defining a data model.
I can’t imagine noodling my way through this in a unit test and then trying to bend Lombok to support my test case. Like I said, it’s easier to define all the properties (version, href, links, and items), then flagging it as a Builder, Data, and Value class. Go into the unit test code, and start using it. Avoid much heartache.
And because I can still submit pull requests with gobs of test cases, achieving 100% coverage of things, I see little value in this “test first” approach advocated by TDD.
So…am I wrong? In what way? Jump in on the comment cause I’m dying to hear.
It may sound quirky, but I had finally HAD IT with a project I manage: Spring Web Services. There were several aspects of the build file that kept getting in my way.
Well, I was about to embark upon a critical change from which Gradle pushed me over the edge. To the point that not only did I convert to Maven, but ALSO moved the reference documentation from DocBook to Asciidoctor.
So what’s wrong with Gradle?
I know this is controversial, but I have worked with many projects. On the Spring team, it’s a common paradigm that if you need either a feature or a bug fix applied to some aspect of the portfolio, you check it out and submit a patch.
The community embraces this, and sometimes, if you really need something, the fastest route is to do it yourself.
Spring Framework, Spring Security, Spring Cloud Stream, Spring Session, Spring Session MongoDB, Spring Data Commons, blah blah blah. Just a handful of the projects I have dipped my toe in, some more than others.
Many use Maven, many use Gradle. I don’t mind Gradle for the occasional patch request. But when it came to an up-and-coming switch Spring Web Services had to make, Gradle wasn’t going to cut it.
Spring Web Services was going to create a new major release. We had supported 2.4 for a couple years, and 3.0 was about to get launched. This was the version where I could bump up the minimum to Java 8, Spring Framework 5, and other key 3rd party libraries.
That wasn’t the tricky part. The fact that we had to maintain the 2.4 branch at the same time, meant I needed a simpler build model.
Gradle comes loaded with a programming model that lets you declare things, and then hook in extra code to tailor things. And that is the problem!
Declarative vs. programmatic
Maven has this pattern of declaring what you want, and it goes and carries it out. You declare things like:
- Artifact coordinates
Because people frequently need to adjust settings for different scenarios, Maven supports the concept of profiles.
Gradle starts with the concept of a declarative build system as well. You can also declare:
- Artifact coordinates
But guess what? No first class support for profiles. Most people say, “no problem! Just code it.” Gradle uses Groovy, so you can write a little glue code, and BAM you have the concept of profiles.
Why do I need profiles? To make backwards compatibility maintainable, I always test each patch against the latest GA release of Spring Framework, as well as the latest snapshot release and the next major version. Each of these, captured in a separate profile, allows me to command my CI server to run every single patch across multiple versions of Spring Framework/Spring Security, and verify I’m not breaking anything.
Gluing this together with Groovy code was okay when I maintained a single branch. But migrating into maintaining two branches (master for 3.0 stuff and 2.x for the 2.4 stuff), that frankly wasn’t going to cut it. You see, Maven profiles are supported inside IntelliJ IDEA, but some glue code that emulates the same concept in Gradle doesn’t work AT ALL. It drives me to use the command line when my IDE should be supporting me.
This isn’t the only thing I’ve seen Gradle come short on where one must write code in their build file.
Frankly, I don’t WANT to write code there. That’s what I was able to do with Ant, and Ant drives one to spend too much time in the build file. Gradle is by no means as bad as Ant, but it seems a tad ant-ish.
Since you asked, there is another irritant. Gradle has plugins and that is great. Except that the blocks they add to your build file are at the global level. That means any construct you spy with settings inside it doesn’t identify the plugin they came from. You want to debug some piece of the build? You can’t jump to the plugin’s docs. You must first rummage around Google to determine which plugin is at hand.
To drive my point home, I’ve checked out projects where the developer built a handful of custom gradle plugins. That’s nice and handy, but it raises the need for me to READ every single plugin to know what’s happening.
Frankly, I’d prefer a block of XML, where the coordinates of the plugin AND its configuration details are RIGHT THERE. Maven may not offer the same magic of codability, but I fear that feature is shadowed by lack of discoverability.
Why the hate?
Okay, I know that Gradle is pretty cool out there. Many have been driven over the cliff by Maven. But to be honest, I haven’t seen enough payoff to warrant sticking with Gradle.
Since I could be wrong, feel free to dive into the comments and give me your pluses and minuses about Gradle vs. Maven!