ワンクリックで
opik-backend
// Java backend patterns for Opik. Use when working in apps/opik-backend, designing APIs, database operations, or services.
// Java backend patterns for Opik. Use when working in apps/opik-backend, designing APIs, database operations, or services.
Python SDK patterns for Opik. Use when working in sdks/python, on SDK APIs, integrations, or message processing.
Generate self-contained HTML architecture diagrams. Use when creating visual diagrams for PRs, task plans, or architectural explanations.
Add analytics events to Opik features. Use when wiring PostHog events on the frontend or backend for product analytics tracking.
Authoring Fern MDX documentation pages for the Opik docs site, plus release-note and changelog routing. Use when writing or updating pages under apps/opik-documentation/documentation/fern/, drafting PR descriptions, or picking the right changelog surface.
React frontend patterns for Opik. Use when working in apps/opik-frontend, on components, state, or data fetching.
Playwright E2E test generation workflow for Opik. Use when generating, fixing, or planning automated tests in tests_end_to_end/.
| name | opik-backend |
| description | Java backend patterns for Opik. Use when working in apps/opik-backend, designing APIs, database operations, or services. |
@InjectTracesResource, SpansResource, DatasetsResource (not TraceResource)TracesResourceTest, SpansResourceTest, DatasetsResourceTest (not TraceResourceTest)/v1/private/traces, /v1/private/spans (not /v1/private/trace)traces, spans, feedback_scores (not trace, span, feedback_score)TraceDAO, SpanDAO, DatasetDAO (not TracesDAO)TraceService, SpanService, DatasetService (not TracesService)// ✅ GOOD
@Path("/v1/private/traces")
public class TracesResource { }
// ✅ GOOD - DAO and Service use singular
public class TraceDAO { }
public class TraceService { }
// ✅ GOOD - test classes match plural resource name
public class TracesResourceTest { }
// ❌ BAD - singular test class
public class TraceResourceTest { }
// ❌ BAD - singular resource/URL
@Path("/v1/private/trace")
public class TraceResource { }
// ❌ BAD - plural DAO/Service
public class TracesDAO { }
public class TracesService { }
@Builder(toBuilder = true)@NonNull on required fields — it generates a runtime null check at construction@Valid cascade (Jakarta validators like @NotNull/@NotBlank/@Size), use Jakarta annotations only — do not stack @NonNull on top. Bean Validation already enforces the contract at the API boundary; doubling up is redundant noise// ✅ GOOD - internal record, Lombok @NonNull
@Builder(toBuilder = true)
record MyData(@NonNull UUID id, @NonNull String name, String description) {}
MyData data = MyData.builder()
.id(id)
.name(name)
.build();
// ✅ GOOD - request-body DTO, Jakarta validators only
@Builder(toBuilder = true)
public record MyRequest(
@NotNull UUID id,
@NotBlank String name,
@NotNull @Size(min = 1, max = 1000) @Valid List<MyItem> items) {}
// ❌ BAD - plain constructor (positional mistakes, less readable)
new MyData(id, name, null);
// ❌ BAD - @Builder without toBuilder
@Builder
record MyData(UUID id, String name) {}
// ❌ BAD - stacking @NonNull and @NotNull on the same field
public record MyRequest(@NonNull @NotNull UUID id) {}
@RequiredArgsConstructor(onConstructor_ = @Inject) instead of manual constructors// ✅ GOOD
@RequiredArgsConstructor(onConstructor_ = @Inject)
public class MyService {
private final @NonNull DependencyA depA;
private final @NonNull DependencyB depB;
}
// ❌ BAD - boilerplate constructor
public class MyService {
private final DependencyA depA;
@Inject
public MyService(DependencyA depA) {
this.depA = depA;
}
}
@NonNull) on interface method parameters// ✅ GOOD
interface MyService {
void process(String workspaceId, UUID promptId);
}
// ❌ BAD - validation on interface
interface MyService {
void process(@NonNull String workspaceId, @NonNull UUID promptId);
}
// ✅ GOOD
var template = TemplateUtils.newST(QUERY);
// ❌ BAD - causes memory leak via STGroup singleton
var template = new ST(QUERY);
// ✅ GOOD
users.getFirst()
users.getLast()
// ❌ BAD
users.get(0)
users.get(users.size() - 1)
// ✅ GOOD - text blocks for multi-line SQL
@SqlQuery("""
SELECT * FROM datasets
WHERE workspace_id = :workspace_id
<if(name)> AND name like concat('%', :name, '%') <endif>
""")
// ❌ BAD - string concatenation
@SqlQuery("SELECT * FROM datasets " +
"WHERE workspace_id = :workspace_id " +
"<if(name)> AND name like concat('%', :name, '%') <endif> ")
// ✅ GOOD
Set.of("A", "B", "C")
List.of(1, 2, 3)
Map.of("key", "value")
// ❌ BAD
Arrays.asList("A", "B", "C")
exclude_category_names not exclude_category_name). Starting with a singular name and later adding a plural variant results in two redundant query params on the same endpoint. Plural names are backward-compatible since they work for both single and multiple values.throw new BadRequestException("Invalid input");
throw new NotFoundException("User not found: '%s'".formatted(id));
throw new ConflictException("Already exists");
throw new InternalServerErrorException("System error", cause);
io.dropwizard.jersey.errors.ErrorMessagecom.comet.opik.api.error.ErrorMessage// ✅ GOOD - values in single quotes
log.info("Created user: '{}'", userId);
log.error("Failed for workspace: '{}'", workspaceId, exception);
// ❌ BAD - no quotes
log.info("Created user: {}", userId);
@RequiredPermissions annotation guidance for endpoints