diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 000d8fdfa..859892b2c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,8 +7,9 @@ This doc is intended for contributors to `cadence-java-client` (hopefully that's
 ## Development Environment
 
 * Java 8.
-* Gradle build tool
+* Gradle build tool 4.5.1
 * Docker
+* Apache Thrift 0.9.3
 
 ## Licence headers
 
diff --git a/src/main/java/com/uber/cadence/activity/ActivityHelper.java b/src/main/java/com/uber/cadence/activity/ActivityHelper.java
new file mode 100644
index 000000000..ec143345b
--- /dev/null
+++ b/src/main/java/com/uber/cadence/activity/ActivityHelper.java
@@ -0,0 +1,34 @@
+/*
+ *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ *  Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"). You may not
+ *  use this file except in compliance with the License. A copy of the License is
+ *  located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ *  or in the "license" file accompanying this file. This file is distributed on
+ *  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ *  express or implied. See the License for the specific language governing
+ *  permissions and limitations under the License.
+ */
+
+package com.uber.cadence.activity;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that this class is an activity helper. This annotation applies only to parent activity
+ * interfaces, where activity methods are defined. Not required. By default all methods owned by an
+ * Activity, are {@link ActivityMethod}, even if is not annotated. Use it to avoid this behavior and
+ * be able to define or implement some commons behavior that could be extended by an Activity Method
+ * interface.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ActivityHelper {}
diff --git a/src/main/java/com/uber/cadence/internal/sync/POJOActivityTaskHandler.java b/src/main/java/com/uber/cadence/internal/sync/POJOActivityTaskHandler.java
index dfb1c8b60..9bb382165 100644
--- a/src/main/java/com/uber/cadence/internal/sync/POJOActivityTaskHandler.java
+++ b/src/main/java/com/uber/cadence/internal/sync/POJOActivityTaskHandler.java
@@ -22,6 +22,7 @@
 import com.uber.cadence.PollForActivityTaskResponse;
 import com.uber.cadence.RespondActivityTaskCompletedRequest;
 import com.uber.cadence.RespondActivityTaskFailedRequest;
+import com.uber.cadence.activity.ActivityHelper;
 import com.uber.cadence.activity.ActivityMethod;
 import com.uber.cadence.client.ActivityCancelledException;
 import com.uber.cadence.common.MethodRetry;
@@ -92,9 +93,16 @@ private void addActivityImplementation(
       if (i.getType().getTypeName().startsWith("org.mockito")) {
         continue;
       }
+
       for (Method method : i.getRawType().getMethods()) {
         ActivityMethod annotation = method.getAnnotation(ActivityMethod.class);
         String activityType;
+
+        ActivityHelper helper = i.getRawType().getAnnotation(ActivityHelper.class);
+        if (helper != null) {
+          continue;
+        }
+
         if (annotation != null && !annotation.name().isEmpty()) {
           activityType = annotation.name();
         } else {
diff --git a/src/test/java/com/uber/cadence/internal/testing/WorkflowTestingTest.java b/src/test/java/com/uber/cadence/internal/testing/WorkflowTestingTest.java
index a1843eef3..fdb4d7f9e 100644
--- a/src/test/java/com/uber/cadence/internal/testing/WorkflowTestingTest.java
+++ b/src/test/java/com/uber/cadence/internal/testing/WorkflowTestingTest.java
@@ -36,6 +36,7 @@
 import com.uber.cadence.WorkflowExecution;
 import com.uber.cadence.WorkflowExecutionInfo;
 import com.uber.cadence.activity.Activity;
+import com.uber.cadence.activity.ActivityHelper;
 import com.uber.cadence.activity.ActivityMethod;
 import com.uber.cadence.activity.ActivityOptions;
 import com.uber.cadence.client.WorkflowClient;
@@ -151,6 +152,53 @@ public void testFailure() {
     }
   }
 
+  @ActivityHelper
+  public interface TestParentActivity {
+    String execute(String input);
+  }
+
+  public interface TestChild1Activity extends TestParentActivity {
+
+    @ActivityMethod(scheduleToCloseTimeoutSeconds = 3600)
+    @Override
+    String execute(String input);
+  }
+
+  public interface TestChild2Activity extends TestParentActivity {
+
+    @ActivityMethod(scheduleToCloseTimeoutSeconds = 3600)
+    @Override
+    String execute(String input);
+  }
+
+  private static class ActivityChild1Impl implements TestChild1Activity {
+
+    @Override
+    public String execute(String input) {
+      return Activity.getTask().getActivityType() + "-" + input;
+    }
+  }
+
+  private static class ActivityChild2Impl implements TestChild2Activity {
+
+    @Override
+    public String execute(String input) {
+      return Activity.getTask().getActivityType() + "-" + input;
+    }
+  }
+
+  @Test
+  public void testChildActivity() {
+    Worker worker = testEnvironment.newWorker(TASK_LIST);
+    worker.registerWorkflowImplementationTypes(ChildActivityWorkflow.class);
+    worker.registerActivitiesImplementations(new ActivityChild1Impl(), new ActivityChild2Impl());
+    testEnvironment.start();
+    WorkflowClient client = testEnvironment.newWorkflowClient();
+    TestWorkflow workflow = client.newWorkflowStub(TestWorkflow.class);
+    String result = workflow.workflow1("input1");
+    assertEquals("TestChild1Activity::execute-input1-TestChild2Activity::execute-input1", result);
+  }
+
   public interface TestActivity {
 
     @ActivityMethod(scheduleToCloseTimeoutSeconds = 3600)
@@ -176,6 +224,18 @@ public String workflow1(String input) {
     }
   }
 
+  public static class ChildActivityWorkflow implements TestWorkflow {
+
+    private final TestChild1Activity activity1 = Workflow.newActivityStub(TestChild1Activity.class);
+    private final TestChild2Activity activity2 = Workflow.newActivityStub(TestChild2Activity.class);
+
+    @Override
+    public String workflow1(String input) {
+      Workflow.sleep(Duration.ofHours(1)); // test time skipping
+      return activity1.execute(input) + "-" + activity2.execute(input);
+    }
+  }
+
   @Test
   public void testActivity() {
     Worker worker = testEnvironment.newWorker(TASK_LIST);