Temporal is a reliable runtime for polyglot, durable, and fault-tolerant applications. Its workflows, activities, schedules, and other abstractions do the heavy lifting, allowing you to focus on business code.
The gist of the story won't be about how Temporal works; for a high-level overview, go here: How it Works | Temporal Technologies
Temporal offers the critical benefits of resilience, durability, and observability, which are essential for projects. Aside from these benefits, Temporal is developer-friendly from the start. However, despite being developer-friendly, there are still pitfalls to avoid.
Here are some good tips for navigating Temporal pitfalls, leveraging its benefits, and easing development.
Create custom data transfer objects
Data sent through each activity needs to be serialized and deserialized. Hence, these objects should be lightweight and serializable. Avoid using big objects, as they can lead to errors.
Activities communicate back and forth with the Temporal server. Arguments flowing into activities need to be deserialized, and responses coming back from activities need to be serialized.
If you don't avoid this bad practice, you'll encounter this issue: "Complete result exceeds size limit". You can find the causes of this error on the Temporal Community support pages.Â
The solution: store blobs instead of using them as activity payloads.
Activity and workflow payloads should be as small as possible. It's always better to return IDs rather than full-blown entity objects from activities. This way, we avoid serialization/deserialization overhead.
Wrap activity arguments into a single object
Activities and workflows are simple methods, meaning you can pass them an arbitrary number of arguments. However, if you don't use a wrapper for arguments, this will break backward compatibility as activities evolve. When there's one wrapper, you can easily change the data without changing the method arguments arity.
This approach is preferred:
Compared to this approach:
This adds a bit of boilerplate, but the future benefits amortize it in the long run. While working with wrapped arguments, make sure they are serializable. All activity response and request properties need to be serializable.
Retrying the above activity would execute each of the activity’s code lines multiple times, which could result in charging the customer multiple times. Breaking each line into a separate activity method will resolve the returnable issues.If indefinite retries are enabled per activity, the activity will retry until it succeeds or a non-retryable error occurs. This enables durable execution and appropriate action during execution. If the issue is resolved (the payment processor issue is resolved, and the charge is completed), the workflow is completed without intervention from customer support. This is especially helpful during downtime of a third-party service or when dealing with flaky services, as no failure occurs on the Temporal’s end at all.
It's not so hard to notice the boilerplate. One must notice the boilerplate that becomes unmanageable at this point. This is one downside of the Temporal Java SDK. As you add more workflows and activities, you'll encounter more boilerplate.
Another downside is that each activity needs to be created as a new stub in each workflow.
For example, this code:
Needs to be duplicated in every workflow that uses this activity.
Using Temporal judiciously will help reduce boilerplate.
Create custom search attributes to ease workflow filtering
Custom search attributes make the UI more digestible when you have many workflows. For example, a Temporal server could have this order of workflows in a development environment.
Default search attributes can help filter this number down. Here are some default search attributes; the most helpful is ExecutionStatus.
One custom search attribute that you could have is the ability to search by part of the workflow ID.
The default search attribute, workflowID, requires you to supply the full workflow ID in the query and doesn’t support partial search.
This is useful as you will most likely encode some identifiers within the workflow ID.
How to define custom search attributes?
Let's say we want to create a partial workflow ID search attribute. Here's the code to register the custom search attribute PartialWorfklowId on the Temporal service.
In the above example, the entire text is searchable by word. Other types can be used as index fields.
Closing words
In addition to these tips, Temporal provides many project samples to minimize the learning curve and improve developer experience.
The samples are available in all widely used languages (Java, Go, .NET), making it easy to learn and quickly prototype.
For Java developers, official Temporal server configuration support exists. You can use spring-boot-autoconfigure-alpha for Spring Boot projects. This library configures you so you don't have to register workflows and activities yourself.
Here is the code to register workflows from a single package automatically:
All you need to do is annotate the relevant workflows and assign them to task queues.
Developer support, polyglot and developer-friendly guides, and easy setup make Temporal a strong contender for any developer's toolkit.
About author
Milos Zivkovic, an experienced Java Software Engineer from our Nis Branch at Symphony, has a background in integrating and developing secure, efficient, and scalable systems using Spring Boot, Temporal, and other modern technologies. He has worked on custom payment flows, custom authentication flows, and custom CI/CD pipelines. Milos aims to consistently deliver high-quality, business-aligned solutions while effectively communicating and implementing meaningful code improvements.